搭建前端环境 注册微信开发者账号 打开微信公总平台,按照流程一步步注册:https://mp.weixin.qq.com/
然后去申请开通三个我们项目会用到的接口 ![[Pasted image 20250417092751.png]]
以及我们需要用到的插件 ![[Pasted image 20250417093027.png]]https://fuwu.weixin.qq.com/search?tab=3&type=&serviceType=3&page=1&kw=腾讯位置服务地图选点 fuwu.weixin.qq.com/search?tab=3&type=&serviceType=3&page=1&kw=微信同声传译
安装 node.js 和微信开发者工具 下载node.js:https://nodejs.org/en/download 版本选择16.20.0 ![[Pasted image 20250417093149.png]]
微信开发者工具也是点击下载一步步走就行
下载两遍,安装两个相同的,方便后期项目两个端口一起调试 ![[Pasted image 20250417091648.png]] ![[Pasted image 20250417091741.png]]
在开发者工具中运行前端代码
另一个也是一样
搭建后端环境 安装软件环境 安装rabbitmq
`docker pull rabbitmq:3.9.0-management
`docker run -d –name=rabbitmq –restart=always -p 5672:5672 -p 15672:15672 rabbitmq:3.9.0-management
1、首先下载rabbitmq_delayed_message_exchange-3.9.0.ez文件上传到RabbitMQ所在服务器,下载地址:https://www.rabbitmq.com/community-plugins.html
2、上传下载延迟队列插件到Linux操作系统中,切换到插件所在目录,
执行 docker cp rabbitmq_delayed_message_exchange-3.9.0.ez rabbitmq:/plugins 命令,将刚插件拷贝到容器内plugins目录下
3、执行 docker exec -it rabbitmq /bin/bash 命令进入到容器内部,并 cd plugins 进入plugins目录
执行 ls -l|grep delay 命令查看插件是否copy成功 在容器内plugins目录下,执行 rabbitmq-plugins enable rabbitmq_delayed_message_exchange 命令启用插件
4、exit命令退出RabbitMQ容器内部,然后执行 docker restart rabbitmq 命令重启RabbitMQ容器
**第四步 远程访问设置凭证1 2 3 4 5 6 7 8 9 10 11 # 进入容器 docker exec -it rabbitmq bash # 创建新用户(用户名:admin,密码:Abc123) rabbitmqctl add_user admin Abc123 # 赋予管理员权限 rabbitmqctl set_user_tags admin administrator # 赋予所有权限(虚拟主机为 /) rabbitmqctl set_permissions -p / admin ".*" ".*" ".*"
安装redis
docker pull redis
1 2 3 4 ## 创建目录 mkdir -p /home/redis/conf ## 创建文件 touch /home/redis/conf/redis.conf
1 2 3 4 5 6 7 8 docker run -d \ --name redis \ -p 6379:6379 \ --restart unless-stopped \ -v /home/redis/data:/data \ -v /home/redis/conf/redis.conf:/etc/redis/redis.conf \ redis:latest \ redis-server /etc/redis/redis.conf
1 2 3 4 5 6 7 8 ### 直接通过Docker Redis 命令进入Redis控制台 docker exec -it redis redis-cli ### 进入 Redis 控制台 redis-cli ### 添加一个变量为 key 为 name , value 为 bella 的内容 > set name bella ### 查看 key 为 name 的 value 值 > get name
安装minio 1 2 3 4 5 6 7 8 9 10 docker run \ --name minio_one \ -p 9000:9000 \ -p 9001:9001 \ -d \ -e "MINIO_ROOT_USER=admin" \ -e "MINIO_ROOT_PASSWORD=admin123456" \ -v /root/minio-data:/data \ -v /root/minio-config:/root/.minio \ minio/minio server /data --console-address ":9001"
docker run:这是Docker命令行工具用来运行一个新容器的命令。
–name minio_one:这个参数为容器指定了一个名称,这里名称被设置为minio_one。使用名称可以更方便地管理容器。
p 9000:9000:这个参数将容器内的9000端口映射到宿主机的9000端口。MinIO服务默认使用9000端口提供API服务。
-p 9001:9001:这个参数将容器内的9001端口映射到宿主机的9001端口。这是MinIO的控制台(Console)端口,用于访问MinIO的图形用户界面。
-d:这个参数告诉Docker以“detached”模式运行容器,即在后台运行。
-e “MINIO_ROOT_USER=admin”:设置环境变量MINIO_ROOT_USER,这是访问MinIO服务的用户名称,这里设置为admin。
-e “MINIO_ROOT_PASSWORD=admin123456”:设置环境变量MINIO_ROOT_PASSWORD,这是访问MinIO服务的用户密码,这里设置为admin123456。
-v /home/data:/data:这个参数将宿主机的目录/home/data:/data挂载到容器的/data目录。MinIO会将所有数据存储在这个目录。
-v /root/config:/root/.minio:这个参数将宿主机的目录/root/minio-config挂载到容器的/root/.minio目录。这个目录用于存储MinIO的配置文件和数据。
minio/minio:这是要运行的Docker镜像的名称,这里使用的是官方发布的MinIO镜像。
server /data:这是传递给MinIO程序的命令行参数,告诉MinIO以服务器模式运行,并且使用/data目录作为其数据存储位置。
–console-address “:9001”:这个参数指定MinIO控制台服务的监听地址和端口。在这个例子中,它设置为监听所有接口上的9001端口。
注意:文件上传时,需要调整-下Linux 服务器的时间与Windows 时间一致!
1 2 3 4 5 6 7 8 9 10 11 12 第一步:安装ntp服务 yum -y install ntp 第二步:开启开机启动服务 systemctl enable ntpd 第三步:启动服务 systemctl start ntpd 第四步:更改时区 timedatectl set-timezone Asia/Shanghai 第五步:启用ntp同步 timedatectl set-ntp yes 第六步:同步时间 ntpq -p
如果启动不了,那么就重构主义,推了重建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 # 强制删除旧容器(保留数据卷) docker rm -f minio_one # 重新运行 MinIO 容器(使用正确的镜像名称 minio/minio) docker run \ --name minio_one \ -p 9000:9000 \ -p 9001:9001 \ -d \ -e "MINIO_ROOT_USER=admin" \ -e "MINIO_ROOT_PASSWORD=admin123456" \ -v /root/minio-data:/data \ -v /root/minio-config:/root/.minio \ minio/minio server /data --console-address ":9001"
nacos
bash /root/nacos/bin/startup.sh -m standalone
bash /root/nacos/bin/shutdown.sh
导入数据库内容
逻辑删除
官方文档:https://baomidou.com/guides/logic-delete/
第一种方法:
1 2 3 4 5 6 7 mybatis-plus: global-config: db-config: logic-delete-field: isDelete logic-delete-value: 1 logic-not-delete-value: 0 id-type: auto
第二种方法: 添加@TableLogic注解
1 2 3 4 5 6 7 8 9 10 @Data public class User {。。。 @TableLogic(value = "1",delval = "0") private Integer isDelete; 。。。 }
添加完后再使用mp已经封装好的语句都会加上 where deleted = 0
分页查询
实现分页查询前需要配置分页插件
分页插件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Configuration @MapperScan("scan.your.mapper.package") public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor () { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor (); interceptor.addInnerInterceptor(new PaginationInnerInterceptor (DbType.MYSQL)); return interceptor; } }
导入配置文件
第一种:像之前一样一条一条创建
第二种:把文件打成压缩包,但是压缩包名称要与组的名字相同(默认是 DEFAULT_GROUP) ![[Pasted image 20250418214153.png]]
客户端登录 微信小程序登录流程
![[Pasted image 20250419174655.png]]
微信小程序登录接口
项目工程结构: ![[Pasted image 20250419180421.png]]
准备工作
在 service-customer 中引入依赖
1 2 3 4 <dependency > <groupId > com.github.binarywang</groupId > <artifactId > weixin-java-miniapp</artifactId > </dependency >
**修改 nacos 配置中心文件
创建配置类来读取配置文件信息
在 service-customer 创建包config,创建类来读取配置文件中的内容
1 2 3 4 5 6 7 8 @Component @Data @ConfigurationProperties(prefix = "wx.miniapp") public class WxConfigProperties { private String appId; private String secret; }
创建微信工具包对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Component public class WxConfigOperator { @Autowired private WxConfigProperties wxConfigProperties; @Bean public WxMaService wxMaService () { WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl (); config.setAppid(wxConfigProperties.getAppId()); config.setSecret(wxConfigProperties.getSecret()); WxMaService service = new WxMaServiceImpl (); service.setWxMaConfig(config); return service; } }
功能实现-基础功能
功能实现-远程调用
service:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 @Slf4j @Service @SuppressWarnings({"unchecked", "rawtypes"}) public class CustomerServiceImpl implements CustomerService { @Autowired private CustomerInfoFeignClient client; @Autowired private RedisTemplate redisTemplate; @Override public String login (String code) { Result<Long> longin = client.longin(code); Integer code1 = longin.getCode(); if (code1 != 200 ){ throw new GuiguException (ResultCodeEnum.DATA_ERROR); } Long customerId = longin.getData(); if (customerId == null ){ throw new GuiguException (ResultCodeEnum.DATA_ERROR); } String token = UUID.randomUUID().toString(); redisTemplate.opsForValue().set(RedisConstant.USER_LOGIN_KEY_PREFIX+token, customerId.toString(), RedisConstant.USER_LOGIN_KEY_TIMEOUT, TimeUnit.SECONDS); return token; } }
功能实现-测试
运行乘客端微信小程序(微信开发者工具)
启动后端服务
启动网关
启动 service-customer 服务
启动 web-customer 服务
把用到的配置文件都检查一遍,重点看看IP地址和开放端口,以及MySQL的远程用户(就是因为这个问题,后面用映射成localhost解决了)
获取客户端登录信息 service-customer 接口
Controller
1 2 3 4 5 6 @Operation(summary = "获取客户端登录信息") @GetMapping("/getCustomerLoginInfo/{customerId}") public Result<CustomerLoginVo> getCustomerLoginInfo (@PathVariable Long customerId) { CustomerLoginVo customerLoginVo = customerInfoService.getCustomerInfoService(customerId); return Result.ok(customerLoginVo); }
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 @Override public CustomerLoginVo getCustomerInfoService (Long customerId) { CustomerInfo info = customerInfoMapper.selectById(customerId); if (info == null ) { throw new RuntimeException ("用户不存在" ); } CustomerLoginVo customerLoginVo = new CustomerLoginVo (); BeanUtils.copyProperties(info, customerLoginVo); customerLoginVo.setIsBindPhone(StringUtils.hasText(info.getPhone())); return customerLoginVo; }
web-customer 接口
controller
1 2 3 4 5 6 7 8 @Operation(summary = "获取客户登录信息") @GetMapping("/getCustomerLoginInfo") public Result<CustomerLoginVo> getCustomerLoginInfo (@RequestHeader(value = "token") String token) { CustomerLoginVo customerLoginVo = customerInfoService.getCustomerLoginInfo(token); return Result.ok(customerLoginVo); }
service
这里的token要和前面设置的一样
redisTemplate.opsForValue().get() 就是把redis当成一个map容器,通过键值对的方式来存储或查询1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Override public CustomerLoginVo getCustomerLoginInfo (String token) { String customerId = (String) redisTemplate.opsForValue().get(RedisConstant.USER_LOGIN_KEY_PREFIX+token); if (!StringUtils.hasText(customerId)){ throw new GuiguException (ResultCodeEnum.DATA_ERROR); } Result<CustomerLoginVo> customerLoginInfo = client.getCustomerLoginInfo(Long.parseLong(customerId)); Integer code = customerLoginInfo.getCode(); if (code != 200 ){ throw new GuiguException (ResultCodeEnum.DATA_ERROR); } CustomerLoginVo data = customerLoginInfo.getData(); if (data == null ){ throw new GuiguException (ResultCodeEnum.DATA_ERROR); } return data; }
登录校验 这里选用 AOP面向切面加自定义注解完成,有想过用拦截器来做,但是路径太多,懒得配置了(高情商,更希望学习底层知识)
创建自定义注解 1 2 3 4 5 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface LoginDetection { }
创建切面类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 @Component @Aspect public class LoginAspect { @Autowired private RedisTemplate redisTemplate; @Around("execution(* com.atguigu.daijia.*.controller.*.*(..)) && @annotation(loginDetection)") public Object login (ProceedingJoinPoint proceedingJoinPoint, LoginDetection loginDetection) throws Throwable { RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); ServletRequestAttributes sra = (ServletRequestAttributes) requestAttributes; String token = sra.getRequest().getHeader("token" ); if (!StringUtils.hasText(token)){ throw new GuiguException (ResultCodeEnum.LOGIN_AUTH); } String customerId = (String) redisTemplate.opsForValue().get(RedisConstant.USER_LOGIN_KEY_PREFIX + token); if (StringUtils.hasText(customerId)){ AuthContextHolder.setUserId(Long.parseLong(customerId)); } return proceedingJoinPoint.proceed(); } }
把之前的getCustomerLoginInfo用上注解 在web-customer中的service里 getCustomerLoginInfo 做修改,用上我们刚刚写的注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Operation(summary = "获取客户登录信息") @LoginDetection @GetMapping("/getCustomerLoginInfo") public Result<CustomerLoginVo> getCustomerLoginInfo () { Long userId = AuthContextHolder.getUserId(); CustomerLoginVo customerLoginVo = customerInfoService.getCustomerInfo(userId); return Result.ok(customerLoginVo); }
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Override public CustomerLoginVo getCustomerInfo (Long customerId) { Result<CustomerLoginVo> customerLoginInfo = client.getCustomerLoginInfo(customerId); Integer code = customerLoginInfo.getCode(); if (code != 200 ){ throw new GuiguException (ResultCodeEnum.DATA_ERROR); } CustomerLoginVo data = customerLoginInfo.getData(); if (data == null ){ throw new GuiguException (ResultCodeEnum.DATA_ERROR); } return data; }
获取乘客手机号 可恶,写完的时候发现个人版的微信开发无法使用这个,白写,最后不做判断直接改成true了
controller 1 2 3 4 5 @Operation(summary = "更新客户微信手机号码") @PostMapping("/updateWxPhoneNumber") public Result<Boolean> updateWxPhoneNumber (@RequestBody UpdateWxPhoneForm updateWxPhoneForm) { return Result.ok(customerInfoService.updateWxPhoneNumber(updateWxPhoneForm)); }
service 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Override public Boolean updateWxPhoneNumber (UpdateWxPhoneForm updateWxPhoneForm) { try { WxMaPhoneNumberInfo phoneNoInfo = wxMaService.getUserService().getPhoneNoInfo(updateWxPhoneForm.getCode()); Long customerId = updateWxPhoneForm.getCustomerId(); CustomerInfo customerInfo = customerInfoMapper.selectById(customerId); customerInfo.setPhone(phoneNoInfo.getPhoneNumber()); customerInfoMapper.updateById(customerInfo); return true ; } catch (WxErrorException e) { throw new RuntimeException (e); } }
web-customer
controller 企业版1 2 3 4 5 6 7 @Operation(summary = "更新用户微信手机号") @LoginDetection @PostMapping("/updateWxPhone") public Result updateWxPhone (@RequestBody UpdateWxPhoneForm updateWxPhoneForm) { updateWxPhoneForm.setCustomerId(AuthContextHolder.getUserId()); return Result.ok(customerInfoService.updateWxPhoneNumber(updateWxPhoneForm)); }
个人版:
1 2 3 4 5 6 7 @Operation(summary = "更新用户微信手机号") @LoginDetection @PostMapping("/updateWxPhone") public Result updateWxPhone (@RequestBody UpdateWxPhoneForm updateWxPhoneForm) { updateWxPhoneForm.setCustomerId(AuthContextHolder.getUserId()); return Result.ok(true ); }
service1 2 3 4 5 @Override public boolean updateWxPhoneNumber (UpdateWxPhoneForm updateWxPhoneForm) { Result<Boolean> booleanResult = client.updateWxPhoneNumber(updateWxPhoneForm); return true ; }
司机端登录 司机开启接单的条件: 1、登录
2、认证通过
3、建立了腾讯云人员库人员
4、当日验证了人脸识别
准备工作与流程
因为司机端要完成认证模块,比如身份证,驾驶证之类的,所以我们需要用到腾讯云的对象存储COS 还有腾讯云的OCR来识别身份证和驾驶证
流程如下
司机端登录功能
获取用户信息
腾讯云对象存储COS
上传信息接口
腾讯云OCR,识别身份证与驾驶证
人脸识别
司机端登录功能 这个步骤跟之前客户端差不多
service:
就是获取code,然后通过code加密钥和小程序id去获取用户的唯一表示openid
判断这个用户是不是第一次登录
是的话初始化用户
返回id
web-service:
获取code,调用service
远程调用获取数据
生成token
放入redis
返回token
service-drive DriverInfoController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Slf4j @Tag(name = "司机API接口管理") @RestController @RequestMapping(value="/driver/info") @SuppressWarnings({"unchecked", "rawtypes"}) public class DriverInfoController { @Autowired private DriverInfoService driverInfoService; @Operation(summary = "小程序授权登录") @GetMapping("/login/{code}") public Result<Long> login (@PathVariable("code") String code) { return Result.ok(driverInfoService.login(code)); } }
DriverInfoServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 @Slf4j @Service @SuppressWarnings({"unchecked", "rawtypes"}) public class DriverInfoServiceImpl extends ServiceImpl <DriverInfoMapper, DriverInfo> implements DriverInfoService { @Autowired private WxMaService wxMaService; @Autowired private DriverInfoMapper driverInfoMapper; @Autowired private DriverSetMapper driverSetMapper; @Autowired private DriverAccountMapper driverAccountMapper; @Autowired private DriverLoginLogMapper driverLoginLogMapper; @Override public Long login (String code) { try { WxMaJscode2SessionResult sessionInfo = wxMaService.getUserService().getSessionInfo(code); String openid = sessionInfo.getOpenid(); LambdaQueryWrapper<DriverInfo> wrapper = new LambdaQueryWrapper <>(); wrapper.eq(DriverInfo::getWxOpenId, openid); DriverInfo driverInfo = driverInfoMapper.selectOne(wrapper); if (driverInfo == null ){ driverInfo = new DriverInfo (); driverInfo.setNickname(String.valueOf(System.currentTimeMillis())); driverInfo.setAvatarUrl("https://oss.aliyuncs.com/aliyun_id_photo_bucket/default_handsome.jpg" ); driverInfo.setWxOpenId(openid); driverInfoMapper.insert(driverInfo); DriverSet driverSet = new DriverSet (); driverSet.setDriverId(driverInfo.getId()); driverSet.setOrderDistance(new BigDecimal (0 )); driverSet.setAcceptDistance(new BigDecimal (SystemConstant.ACCEPT_DISTANCE)); driverSet.setIsAutoAccept(0 ); driverSetMapper.insert(driverSet); DriverAccount driverAccount = new DriverAccount (); driverAccount.setDriverId(driverInfo.getId()); driverAccountMapper.insert(driverAccount); } DriverLoginLog loginLog = new DriverLoginLog (); loginLog.setDriverId(driverInfo.getId()); loginLog.setMsg("小程序登录" ); driverLoginLogMapper.insert(loginLog); return driverInfo.getId(); } catch (WxErrorException e) { throw new GuiguException (ResultCodeEnum.DATA_ERROR); } } }
service-drive-client 1 2 3 4 5 6 7 @FeignClient(value = "service-driver") public interface DriverInfoFeignClient { @GetMapping("/driver/info/login/{code}") public Result<Long> login (@PathVariable("code") String code) ; }
web-drive controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Slf4j @Tag(name = "司机API接口管理") @RestController @RequestMapping(value="/driver") @SuppressWarnings({"unchecked", "rawtypes"}) public class DriverController { @Autowired private DriverService driverService; @Operation(summary = "小程序授权登录") @GetMapping("/login/{code}") public Result<String> login (@PathVariable("code") String code) { return Result.ok(driverService.login(code)); } }
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 @Slf4j @Service @SuppressWarnings({"unchecked", "rawtypes"}) public class DriverServiceImpl implements DriverService { @Autowired private DriverInfoFeignClient driverInfoFeignClient; @Autowired private RedisTemplate redisTemplate; @Override public String login (String code) { Result<Long> result = driverInfoFeignClient.login(code); if (result.getCode() != 200 ){ throw new GuiguException (ResultCodeEnum.DATA_ERROR); } Long data = result.getData(); if (data == null ){ throw new GuiguException (ResultCodeEnum.DATA_ERROR); } String token = UUID.randomUUID().toString().replace("-" ,"" ); redisTemplate.opsForValue().set(RedisConstant.USER_LOGIN_KEY_PREFIX + token, data.toString(), RedisConstant.USER_LOGIN_KEY_TIMEOUT, TimeUnit.SECONDS); return token; } }
获取登录司机信息 service-driver controller
1 2 3 4 5 6 @Operation(summary = "获取司机登录信息") @GetMapping("/getDriverLoginInfo/{driverId}") public Result<DriverLoginVo> getDriverInfo (@PathVariable("driverId") long driverId) { DriverLoginVo driverLoginVo = driverInfoService.getDriverInfo(driverId); return Result.ok(driverLoginVo); }
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Override public DriverLoginVo getDriverInfo (long driverId) { DriverInfo driverInfo = driverInfoMapper.selectById(driverId); DriverLoginVo driverLoginVo = new DriverLoginVo (); BeanUtils.copyProperties(driverInfo, driverLoginVo); String faceModelId = driverInfo.getFaceModelId(); boolean isArchiveFace = StringUtils.hasText(faceModelId); driverLoginVo.setIsArchiveFace(isArchiveFace); return driverLoginVo; }
service-driver-client
1 2 3 4 5 6 7 8 9 10 11 @FeignClient(value = "service-driver") public interface DriverInfoFeignClient { @GetMapping("/driver/info/login/{code}") public Result<Long> login (@PathVariable("code") String code) ; @GetMapping("/driver/info/getDriverLoginInfo/{driverId}") public Result<DriverLoginVo> getDriverInfo (@PathVariable("driverId") long driverId) ; }
web-driver controller
1 2 3 4 5 6 @Operation(summary = "获取登录司机信息") @LoginDetection @GetMapping("/getDriverLoginInfo") public Result<DriverLoginVo> getDriverLoginInfo () { return Result.ok(driverService.getDriverLoginVo()); }
service
1 2 3 4 5 6 @Override public DriverLoginVo getDriverLoginVo () { Long driverId = AuthContextHolder.getUserId(); Result<DriverLoginVo> driverInfo = driverInfoFeignClient.getDriverInfo(driverId); return driverInfo.getData(); }
测试一下前面的代码
打开微信开发者工具
改好相关配置文件
nacos
启动后端服务
网关服务service-gateway
司机端基础服务service-driver
对外访问服务web-driver
可恶,在这里前面web-driver里面controller的路径写错了,一直访问不上,还有nacos里面的配置忘记修改成映射后的端口
开通腾讯云对象存储COS 准备工作
注册并实名腾讯云
找到对象存储
创建储存桶
创建并保存密钥和id
思路 在 web-driver:
获取文件以及存放路径
远程调用实现文件上传
返回 vo 对象
在 service-driver:
导入依赖
获取上传文件及路径
修改配置文件
基于腾讯云官方文档调用实现上传
返回 vo 对象
注意点:因为保密隐私,我们这里的存储桶肯定是私密的,但是我们又要让用户能看到上传后的结果,因此我们需要创建一个临时地址,让客户看到存储内容
web-service
controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Tag(name = "上传管理接口") @RestController @RequestMapping("file") public class FileController { @Autowired private CosService cosService; @Operation(summary = "上传") @LoginDetection @PostMapping("/upload") public Result<CosUploadVo> upload (@RequestPart("file") MultipartFile file, @RequestParam(value = "path", defaultValue = "auth") String path) { CosUploadVo cosUploadVo = cosService.uploadFile(file, path); return Result.ok(cosUploadVo); } }
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Slf4j @Service @SuppressWarnings({"unchecked", "rawtypes"}) public class CosServiceImpl implements CosService { @Autowired private CosFeignClient cosFeignClient; @Override public CosUploadVo uploadFile (MultipartFile file, String path) { Result<CosUploadVo> result = cosFeignClient.upload(file, path); CosUploadVo cosUploadVo = result.getData(); return cosUploadVo; } }
service-driver-client 1 2 3 4 5 6 7 8 9 10 11 @FeignClient(value = "service-driver") public interface CosFeignClient { @PostMapping(value = "/cos/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public Result<CosUploadVo> upload (@RequestPart("file") MultipartFile file, @RequestParam(value = "path") String path) ;}
service-driver
导入依赖
1 2 3 4 5 <dependency > <groupId > com.qcloud</groupId > <artifactId > cos_api</artifactId > <version > 5.6.227</version > </dependency >
修改配置文件
创建类读取配置文件内容
1 2 3 4 5 6 7 8 9 @Data @Component @ConfigurationProperties(prefix = "tencent.cloud") public class TencentCloudProperties { private String secretId; private String secretKey; private String region; private String bucketPrivate; }
controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Slf4j @Tag(name = "腾讯云cos上传接口管理") @RestController @RequestMapping(value="/cos") @SuppressWarnings({"unchecked", "rawtypes"}) public class CosController { @Autowired private CosService cosService; @Operation(summary = "上传") @PostMapping("/upload") public Result<CosUploadVo> upload (@RequestPart("file") MultipartFile file, @RequestParam("path") String path) { CosUploadVo cosUploadVo = cosService.upload(file, path); return Result.ok(cosUploadVo); } }
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 @Slf4j @Service @SuppressWarnings({"unchecked", "rawtypes"}) public class CosServiceImpl implements CosService { @Autowired private TencentCloudProperties tencentCloudProperties; @Override public CosUploadVo upload (MultipartFile file, String path) { COSClient cosClient = getCosClient(); ObjectMetadata metadata = new ObjectMetadata (); metadata.setContentLength(file.getSize()); metadata.setContentEncoding("UTF-8" ); metadata.setContentType(file.getContentType()); String fileType = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("." )); String uploadPath = "/driver/" + path + "/" + UUID.randomUUID().toString().replaceAll("-" , "" ) + fileType; PutObjectRequest putObjectRequest = null ; try { putObjectRequest = new PutObjectRequest (tencentCloudProperties.getBucketPrivate(), uploadPath, file.getInputStream(), metadata); } catch (IOException e) { throw new RuntimeException (e); } putObjectRequest.setStorageClass(StorageClass.Standard); PutObjectResult putObjectResult = cosClient.putObject(putObjectRequest); cosClient.shutdown(); CosUploadVo cosUploadVo = new CosUploadVo (); cosUploadVo.setUrl(uploadPath); cosUploadVo.setShowUrl(this .getImagerUrl(uploadPath)); return cosUploadVo; }
提取CosClient
1 2 3 4 5 6 7 8 9 10 11 12 13 14 private COSClient getCosClient () { String secretId = tencentCloudProperties.getSecretId(); String secretKey = tencentCloudProperties.getSecretKey(); COSCredentials cred = new BasicCOSCredentials (secretId, secretKey); Region region = new Region (tencentCloudProperties.getRegion()); ClientConfig clientConfig = new ClientConfig (region); clientConfig.setHttpProtocol(HttpProtocol.https); COSClient cosClient = new COSClient (cred, clientConfig); return cosClient; }
获取临时签名url
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Override public String getImagerUrl (String path) { if (!StringUtils.hasText(path)){ return "" ; } COSClient cosClient = getCosClient(); GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest (tencentCloudProperties.getBucketPrivate(), path, HttpMethodName.GET); DateTime dateTime = new DateTime ().plusMinutes(15 ); request.setExpiration(dateTime.toDate()); URL url = cosClient.generatePresignedUrl(request); cosClient.shutdown(); return url.toString(); }
这一大段看着很复杂,其实大部分都是腾讯云官方文档里面的内容,我们只需要修改一下参数即可
腾讯云身份证与驾驶证识别接口 思路 司机注册成功后,应该需要去实名认证,这里可以用到腾讯云的身份识别和云存储功能,身份证和驾驶证上传的步骤都差不多,这里就写在一起了
那我们现在计划很明确了
web-driver:
service-driver:
上传认证图片
调用腾讯云相关接口完成认证
返回认证信息
service-driver
controller:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Slf4j @Tag(name = "腾讯云识别接口管理") @RestController @RequestMapping(value="/ocr") @SuppressWarnings({"unchecked", "rawtypes"}) public class OcrController { @Autowired private OcrService ocrService; @Operation(summary = "身份证识别") @PostMapping("/idCardOcr") public Result<IdCardOcrVo> idCardOcr (@RequestPart("file") MultipartFile file) { IdCardOcrVo idCardOcrVo = ocrService.idCardOcr(file); return Result.ok(idCardOcrVo); } @Operation(summary = "驾驶证识别") @PostMapping("/driverLicenseOcr") public Result<DriverLicenseOcrVo> driverLicenseOcr (@RequestPart("file") MultipartFile file) { DriverLicenseOcrVo driverLicenseOcrVo = ocrService.driverLicenseOcr(file); return Result.ok(driverLicenseOcrVo); } }
service: 导入依赖:
1 2 3 4 5 <dependency> <groupId>com.tencentcloudapi</groupId> <artifactId>tencentcloud-sdk-java</artifactId> <version>${tencentcloud.version}</version> </dependency>
看着代码很多,其实全都是从腾讯云官网拉过来修改配置的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 @Slf4j @Service @SuppressWarnings({"unchecked", "rawtypes"}) public class OcrServiceImpl implements OcrService { @Autowired private TencentCloudProperties tencentCloudProperties; @Autowired private CosService cosService; @Override public IdCardOcrVo idCardOcr (MultipartFile file) { try { byte [] base64 = Base64.encodeBase64(file.getBytes()); String fileBase64 = new String (base64); Credential cred = new Credential (tencentCloudProperties.getSecretId(), tencentCloudProperties.getSecretKey()); HttpProfile httpProfile = new HttpProfile (); httpProfile.setEndpoint("ocr.tencentcloudapi.com" ); ClientProfile clientProfile = new ClientProfile (); clientProfile.setHttpProfile(httpProfile); OcrClient client = new OcrClient (cred,tencentCloudProperties.getRegion(), clientProfile); IDCardOCRRequest req = new IDCardOCRRequest (); req.setImageBase64(fileBase64); IDCardOCRResponse resp = client.IDCardOCR(req); IdCardOcrVo idCardOcrVo = new IdCardOcrVo (); if (StringUtils.hasText(resp.getName())) { idCardOcrVo.setName(resp.getName()); idCardOcrVo.setGender("男" .equals(resp.getSex()) ? "1" : "2" ); idCardOcrVo.setBirthday(DateTimeFormat.forPattern("yyyy/MM/dd" ).parseDateTime(resp.getBirth()).toDate()); idCardOcrVo.setIdcardNo(resp.getIdNum()); idCardOcrVo.setIdcardAddress(resp.getAddress()); CosUploadVo cosUploadVo = cosService.upload(file, "idCard" ); idCardOcrVo.setIdcardFrontUrl(cosUploadVo.getUrl()); idCardOcrVo.setIdcardFrontShowUrl(cosUploadVo.getShowUrl()); } else { String idcardExpireString = resp.getValidDate().split("-" )[1 ]; idCardOcrVo.setIdcardExpire(DateTimeFormat.forPattern("yyyy.MM.dd" ).parseDateTime(idcardExpireString).toDate()); CosUploadVo cosUploadVo = cosService.upload(file, "idCard" ); idCardOcrVo.setIdcardBackUrl(cosUploadVo.getUrl()); idCardOcrVo.setIdcardBackShowUrl(cosUploadVo.getShowUrl()); } return idCardOcrVo; } catch (Exception e) { throw new GuiguException (ResultCodeEnum.DATA_ERROR); } } @Override public DriverLicenseOcrVo driverLicenseOcr (MultipartFile file) { try { byte [] base64 = Base64.encodeBase64(file.getBytes()); String fileBase64 = new String (base64); Credential cred = new Credential (tencentCloudProperties.getSecretId(), tencentCloudProperties.getSecretKey()); HttpProfile httpProfile = new HttpProfile (); httpProfile.setEndpoint("ocr.tencentcloudapi.com" ); ClientProfile clientProfile = new ClientProfile (); clientProfile.setHttpProfile(httpProfile); OcrClient client = new OcrClient (cred, tencentCloudProperties.getRegion(), clientProfile); DriverLicenseOCRRequest req = new DriverLicenseOCRRequest (); req.setImageBase64(fileBase64); DriverLicenseOCRResponse resp = client.DriverLicenseOCR(req); DriverLicenseOcrVo driverLicenseOcrVo = new DriverLicenseOcrVo (); if (StringUtils.hasText(resp.getName())) { driverLicenseOcrVo.setName(resp.getName()); driverLicenseOcrVo.setDriverLicenseClazz(resp.getClass_()); driverLicenseOcrVo.setDriverLicenseNo(resp.getCardCode()); driverLicenseOcrVo.setDriverLicenseIssueDate(DateTimeFormat.forPattern("yyyy-MM-dd" ).parseDateTime(resp.getDateOfFirstIssue()).toDate()); driverLicenseOcrVo.setDriverLicenseExpire(DateTimeFormat.forPattern("yyyy-MM-dd" ).parseDateTime(resp.getEndDate()).toDate()); CosUploadVo cosUploadVo = cosService.upload(file, "driverLicense" ); driverLicenseOcrVo.setDriverLicenseFrontUrl(cosUploadVo.getUrl()); driverLicenseOcrVo.setDriverLicenseFrontShowUrl(cosUploadVo.getShowUrl()); } else { CosUploadVo cosUploadVo = cosService.upload(file, "driverLicense" ); driverLicenseOcrVo.setDriverLicenseBackUrl(cosUploadVo.getUrl()); driverLicenseOcrVo.setDriverLicenseBackShowUrl(cosUploadVo.getShowUrl()); } return driverLicenseOcrVo; } catch (Exception e) { e.printStackTrace(); throw new GuiguException (ResultCodeEnum.DATA_ERROR); } } }
service-client 1 2 3 4 5 6 7 8 9 @FeignClient(value = "service-driver") public interface OcrFeignClient { @PostMapping(value = "/ocr/idCardOcr", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) Result<IdCardOcrVo> idCardOcr (@RequestPart("file") MultipartFile file) ; @PostMapping(value = "/ocr/driverLicenseOcr", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) Result<DriverLicenseOcrVo> driverLicenseOcr (@RequestPart("file") MultipartFile file) ; }
web-service
controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Slf4j @Tag(name = "腾讯云识别接口管理") @RestController @RequestMapping(value="/ocr") @SuppressWarnings({"unchecked", "rawtypes"}) public class OcrController { @Autowired private OcrService ocrService; @Operation(summary = "身份证识别") @LoginDetection @PostMapping("/idCardOcr") public Result<IdCardOcrVo> uploadDriverLicenseOcr (@RequestPart("file") MultipartFile file) { return Result.ok(ocrService.idCardOcr(file)); } @Operation(summary = "驾驶证识别") @LoginDetection @PostMapping("/driverLicenseOcr") public Result<DriverLicenseOcrVo> driverLicenseOcr (@RequestPart("file") MultipartFile file) { return Result.ok(ocrService.driverLicenseOcr(file)); } }
serivce
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Slf4j @Service @SuppressWarnings({"unchecked", "rawtypes"}) public class OcrServiceImpl implements OcrService { @Autowired private OcrFeignClient ocrFeignClient; @Override public IdCardOcrVo idCardOcr (MultipartFile file) { Result<IdCardOcrVo> ocrVoResult = ocrFeignClient.idCardOcr(file); return ocrVoResult.getData(); } @Override public DriverLicenseOcrVo driverLicenseOcr (MultipartFile file) { Result<DriverLicenseOcrVo> result = ocrFeignClient.driverLicenseOcr(file); return result.getData(); } }
获取司机认证信息 思路 司机登录后会进入认证界面,判单是否认证,进入认证界面是会显示回显的图片
其实这个很简单,因为腾讯云的那个文字识别会返回参数出来,我们只需要把回显地址返回回去就行
driver-service
driver-web
service-driver
controller
1 2 3 4 5 6 @Operation(summary = "获取司机认证信息") @GetMapping("/getDriverAuthInfo/{driverId}") public Result<DriverAuthInfoVo> getDriverAuthInfo (@PathVariable Long driverId) { DriverAuthInfoVo driverAuthInfoVo = driverInfoService.getDriverAuthInfo(driverId); return Result.ok(driverAuthInfoVo); }
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ```java @Override public DriverAuthInfoVo getDriverAuthInfo (Long driverId) { DriverInfo driverInfo = driverInfoMapper.selectById(driverId); DriverAuthInfoVo driverAuthInfoVo = new DriverAuthInfoVo (); BeanUtils.copyProperties(driverInfo,driverAuthInfoVo); driverAuthInfoVo.setIdcardBackShowUrl(cosService.getImageUrl(driverAuthInfoVo.getIdcardBackUrl())); driverAuthInfoVo.setIdcardFrontShowUrl(cosService.getImageUrl(driverAuthInfoVo.getIdcardFrontUrl())); driverAuthInfoVo.setIdcardHandShowUrl(cosService.getImageUrl(driverAuthInfoVo.getIdcardHandUrl())); driverAuthInfoVo.setDriverLicenseFrontShowUrl(cosService.getImageUrl(driverAuthInfoVo.getDriverLicenseFrontUrl())); driverAuthInfoVo.setDriverLicenseBackShowUrl(cosService.getImageUrl(driverAuthInfoVo.getDriverLicenseBackUrl())); driverAuthInfoVo.setDriverLicenseHandShowUrl(cosService.getImageUrl(driverAuthInfoVo.getDriverLicenseHandUrl())); return driverAuthInfoVo; }
service-client 1 2 @GetMapping("/driver/info/getDriverAuthInfo/{driverId}") Result<DriverAuthInfoVo> getDriverAuthInfo (@PathVariable("driverId") Long driverId) ;
web-serive
controller
1 2 3 4 5 6 7 8 @Operation(summary = "获取司机认证信息") @GuiguLogin @GetMapping("/getDriverAuthInfo") public Result<DriverAuthInfoVo> getDriverAuthInfo () { Long driverId = AuthContextHolder.getUserId(); return Result.ok(driverService.getDriverAuthInfo(driverId)); }
serivce
1 2 3 4 5 6 7 @Override public DriverAuthInfoVo getDriverAuthInfo (Long driverId) { Result<DriverAuthInfoVo> authInfoVoResult = driverInfoFeignClient.getDriverAuthInfo(driverId); DriverAuthInfoVo driverAuthInfoVo = authInfoVoResult.getData(); return driverAuthInfoVo; }
修改司机认证信息 思路 前端点击提交后,后端需要更新客户认证信息 0: 未认证 【刚注册完为未认证状态】 1:审核中 【提交了认证信息后变为审核中】 2:认证通过 【后台审核通过】 -1:认证未通过【后台审核不通过】
这个和上面差不多,其他就不写了,就写一个service
1 2 3 4 5 6 7 8 9 @Override public boolean updateDriverAuthInfo (UpdateDriverAuthInfoForm update) { Long driverId = update.getDriverId(); DriverInfo driverInfo = new DriverInfo (); driverInfo.setId(driverId); BeanUtils.copyProperties(update, driverInfo); return this .updateById(driverInfo); }
开通人脸识别 腾讯云文档:人脸识别_人脸搜索_人脸检测_人脸比对-腾讯云
1、开通腾讯云人脸识别 2、创建人员库
创建司机人脸模型 创建人脸识别模型之后,腾讯云返回模型id,获取模型id,更新到数据库表,但是因为这是调用腾讯云接口,所以如果之前已经有人拿这张图片创建过则不会创建新的模型id,而是返回之前创建的id
service-driver 后面都是只写重要的代码实现,一般的调用就不写了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 @Override public Boolean creatDriverFaceModel (DriverFaceModelForm driverFaceModelForm) { DriverInfo driverInfo = driverInfoMapper.selectById(driverFaceModelForm.getDriverId()); try { Credential cred = new Credential (tencentCloudProperties.getSecretId(), tencentCloudProperties.getSecretKey()); HttpProfile httpProfile = new HttpProfile (); httpProfile.setEndpoint("iai.tencentcloudapi.com" ); ClientProfile clientProfile = new ClientProfile (); clientProfile.setHttpProfile(httpProfile); IaiClient client = new IaiClient (cred, tencentCloudProperties.getRegion(), clientProfile); CreatePersonRequest req = new CreatePersonRequest (); req.setGroupId(tencentCloudProperties.getPersionGroupId()); req.setPersonId(String.valueOf(driverInfo.getId())); req.setGender(Long.parseLong(driverInfo.getGender())); req.setQualityControl(4L ); req.setUniquePersonControl(4L ); req.setPersonName(driverInfo.getName()); req.setImage(driverFaceModelForm.getImageBase64()); CreatePersonResponse resp = client.CreatePerson(req); System.out.println(AbstractModel.toJsonString(resp)); String faceId = resp.getFaceId(); if (StringUtils.hasText(faceId)) { driverInfo.setFaceModelId(faceId); driverInfoMapper.updateById(driverInfo); } } catch (TencentCloudSDKException e) { e.printStackTrace(); return false ; } return true ; }
预估订单数据 查找客户端当前订单 当一个客户发起订单前先要查询是否有发起并且没有完成的订单,如果有订单未完成,则会弹出进行中的订单
预估驾驶路线
下面实现接口,具体内容就是请求腾讯云提供的接口,按照接口传参数,然后返回需要的结果
腾讯官方文档:WebService API | 腾讯位置服务
编写配置类
1 2 3 4 5 6 7 8 @Configuration public class MyConfig { @Bean public RestTemplate restTemplate () { return new RestTemplate (); } }
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 @Slf4j @Service @SuppressWarnings({"unchecked", "rawtypes"}) public class MapServiceImpl implements MapService { @Autowired private RestTemplate restTemplate; @Value("${tencent.map.key}") private String key; @Override public DrivingLineVo calculateDrivingLine (CalculateDrivingLineForm calculateDrivingLineForm) { String url = "https://apis.map.qq.com/ws/direction/v1/driving/?from={from}&to={to}&key={key}" ; Map<String,String> map = new HashMap (); map.put("from" ,calculateDrivingLineForm.getStartPointLatitude()+"," +calculateDrivingLineForm.getStartPointLongitude()); map.put("to" ,calculateDrivingLineForm.getEndPointLatitude()+"," +calculateDrivingLineForm.getEndPointLongitude()); map.put("key" ,key); int status = Objects.requireNonNull(result).getIntValue("status" ); if (status != 0 ) { throw new GuiguException (ResultCodeEnum.MAP_FAIL); } JSONObject route = result.getJSONObject("result" ).getJSONArray("routes" ).getJSONObject(0 ); DrivingLineVo drivingLineVo = new DrivingLineVo (); drivingLineVo.setDuration(route.getBigDecimal("duration" )); .divide(new BigDecimal (1000 )) .setScale(2 , RoundingMode.HALF_UP)); drivingLineVo.setPolyline(route.getJSONArray("polyline" )); return drivingLineVo; } }
预估订单金额 整合规则引擎Drools
引入依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <dependencies > <dependency > <groupId > org.drools</groupId > <artifactId > drools-core</artifactId > </dependency > <dependency > <groupId > org.drools</groupId > <artifactId > drools-compiler</artifactId > </dependency > <dependency > <groupId > org.drools</groupId > <artifactId > drools-decisiontables</artifactId > </dependency > <dependency > <groupId > org.drools</groupId > <artifactId > drools-mvel</artifactId > </dependency > </dependencies >
创建Drools配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Configuration public class DroolsConfig { private static final String RULES_CUSTOMER_RULES_DRL = "rules/FeeRule.drl" ; @Bean public KieContainer kieContainer () { KieServices kieServices = KieServices.Factory.get(); KieFileSystem kieFileSystem = kieServices.newKieFileSystem(); kieFileSystem.write(ResourceFactory.newClassPathResource(RULES_CUSTOMER_RULES_DRL)); KieBuilder kb = kieServices.newKieBuilder(kieFileSystem); kb.buildAll(); KieModule kieModule = kb.getKieModule(); KieContainer kieContainer = kieServices.newKieContainer(kieModule.getReleaseId()); return kieContainer; } }
创建规则文件
封装费用规则接口 实体类
两个实体类,输入对象和输出对象
输入对象
1 2 3 4 5 6 7 8 9 10 11 12 @Data public class FeeRuleRequest { @Schema(description = "代驾里程") private BigDecimal distance; @Schema(description = "代驾时间") private String startTime; @Schema(description = "等候分钟") private Integer waitMinute; }
输出对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 @Data public class FeeRuleResponse { @Schema(description = "总金额") private BigDecimal totalAmount; @Schema(description = "里程费") private BigDecimal distanceFee; @Schema(description = "等时费用") private BigDecimal waitFee; @Schema(description = "远程费") private BigDecimal longDistanceFee; @Schema(description = "基础里程(公里)") private BigDecimal baseDistance; @Schema(description = "基础里程费(元)") private BigDecimal baseDistanceFee; @Schema(description = "超出基础里程的里程(公里)") private BigDecimal exceedDistance; @Schema(description = "超出基础里程的价格(元/公里)") private BigDecimal exceedDistancePrice; @Schema(description = "基础等时分钟(分钟)") private Integer baseWaitMinute; @Schema(description = "超出基础等时的分钟(分钟)") private Integer exceedWaitMinute; @Schema(description = "超出基础分钟的价格(元/分钟)") private BigDecimal exceedWaitMinutePrice; @Schema(description = "基础远途里程(公里)") private BigDecimal baseLongDistance; @Schema(description = "超出基础远程里程的里程(公里)") private BigDecimal exceedLongDistance; @Schema(description = "超出基础远程里程的价格(元/公里)") private BigDecimal exceedLongDistancePrice; }
费用规则文件 1.起步价 00:00:00-06:59:59 19元(含3公里) 07:00:00-23:59:59 19元(含5公里) 2.里程费 超出起步里程后开始计算 00:00:00-06:59:59 4元/1公里 07:00:00-23:59:59 3元/1公里 3.等候费 等候10分钟后 1元/1分钟 4.远途费 订单行程超出12公里后每公里1元 5.计算总金额 订单总金额 = 基础里程费 + 超出基础里程的费 + 等候费 + 远程费
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 //package对应的不一定是真正的目录,可以任意写com.abc,同一个包下的drl文件可以相互访问 package com.atguigu.daijia import com.atguigu.daijia.model.form.rules.FeeRuleRequest; import java.math.BigDecimal; import java.math.RoundingMode; global com.atguigu.daijia.model.vo.rules.FeeRuleResponse feeRuleResponse; /** 1.起步价 00:00:00-06:59:59 19元(含3公里) 07:00:00-23:59:59 19元(含5公里) */ rule "起步价 00:00:00-06:59:59 19元(含3公里)" salience 10 //指定优先级,数值越大优先级越高,不指定的情况下由上到下执行 no-loop true //防止陷入死循环 when /*规则条件,到工作内存中查找FeeRuleRequest对象 里面出来的结果只能是ture或者false $rule是绑定变量名,可以任意命名,官方推荐$符号,定义了绑定变量名,可以在then部分操作fact对象*/ $rule:FeeRuleRequest(startTime >= "00:00:00" && startTime <= "06:59:59") then feeRuleResponse.setBaseDistance(new BigDecimal("3.0")); feeRuleResponse.setBaseDistanceFee(new BigDecimal("19.0")); //3公里内里程费为0 feeRuleResponse.setExceedDistance(new BigDecimal("0.0")); feeRuleResponse.setExceedDistancePrice(new BigDecimal("4.0")); System.out.println("00:00:00-06:59:59 " + feeRuleResponse.getBaseDistance() + "公里,起步价:" + feeRuleResponse.getBaseDistanceFee() + "元"); end rule "起步价 07:00:00-23:59:59 19元(含5公里)" salience 10 //指定优先级,数值越大优先级越高,不指定的情况下由上到下执行 no-loop true //防止陷入死循环 when /*规则条件,到工作内存中查找FeeRuleRequest对象 里面出来的结果只能是ture或者false $rule是绑定变量名,可以任意命名,官方推荐$符号,定义了绑定变量名,可以在then部分操作fact对象*/ $rule:FeeRuleRequest(startTime >= "07:00:00" && startTime <= "23:59:59") then feeRuleResponse.setBaseDistance(new BigDecimal("5.0")); feeRuleResponse.setBaseDistanceFee(new BigDecimal("19.0")); //5公里内里程费为0 feeRuleResponse.setExceedDistance(new BigDecimal("0.0")); feeRuleResponse.setExceedDistancePrice(new BigDecimal("3.0")); System.out.println("07:00:00-23:59:59 " + feeRuleResponse.getBaseDistance() + "公里,起步价:" + feeRuleResponse.getBaseDistanceFee() + "元"); end /** 2.里程费 超出起步里程后开始计算 00:00:00-06:59:59 4元/1公里 07:00:00-23:59:59 3元/1公里 */ rule "里程费 00:00:00-06:59:59 4元/1公里" salience 10 //指定优先级,数值越大优先级越高,不指定的情况下由上到下执行 no-loop true //防止陷入死循环 when /*规则条件,到工作内存中查找FeeRuleRequest对象 里面出来的结果只能是ture或者false $rule是绑定变量名,可以任意命名,官方推荐$符号,定义了绑定变量名,可以在then部分操作fact对象*/ $rule:FeeRuleRequest(startTime >= "00:00:00" && startTime <= "06:59:59" && distance.doubleValue() > 3.0) then BigDecimal exceedDistance = $rule.getDistance().subtract(new BigDecimal("3.0")); feeRuleResponse.setExceedDistance(exceedDistance); feeRuleResponse.setExceedDistancePrice(new BigDecimal("4.0")); System.out.println("里程费,超出里程:" + feeRuleResponse.getExceedDistance() + "公里,单价:" + feeRuleResponse.getExceedDistancePrice()); end rule "里程费 07:00:00-23:59:59 3元/1公里" salience 10 //指定优先级,数值越大优先级越高,不指定的情况下由上到下执行 no-loop true //防止陷入死循环 when /*规则条件,到工作内存中查找FeeRuleRequest对象 里面出来的结果只能是ture或者false $rule是绑定变量名,可以任意命名,官方推荐$符号,定义了绑定变量名,可以在then部分操作fact对象*/ $rule:FeeRuleRequest(startTime >= "07:00:00" && startTime <= "23:59:59" && distance.doubleValue() > 5.0) then BigDecimal exceedDistance = $rule.getDistance().subtract(new BigDecimal("5.0")); feeRuleResponse.setExceedDistance(exceedDistance); feeRuleResponse.setExceedDistancePrice(new BigDecimal("3.0")); System.out.println("里程费,超出里程:" + feeRuleResponse.getExceedDistance() + "公里,单价:" + feeRuleResponse.getExceedDistancePrice()); end /** 3.等候费 等候10分钟后 1元/1分钟 */ rule "等候费 等候10分钟后 1元/1分钟" salience 10 //指定优先级,数值越大优先级越高,不指定的情况下由上到下执行 no-loop true //防止陷入死循环 when /*规则条件,到工作内存中查找FeeRuleRequest对象 里面出来的结果只能是ture或者false $rule是绑定变量名,可以任意命名,官方推荐$符号,定义了绑定变量名,可以在then部分操作fact对象*/ $rule:FeeRuleRequest(waitMinute > 10) then Integer exceedWaitMinute = $rule.getWaitMinute() - 10; feeRuleResponse.setBaseWaitMinute(10); feeRuleResponse.setExceedWaitMinute(exceedWaitMinute); feeRuleResponse.setExceedWaitMinutePrice(new BigDecimal("1.0")); System.out.println("等候费,超出分钟:" + feeRuleResponse.getExceedWaitMinute() + "分钟,单价:" + feeRuleResponse.getExceedWaitMinutePrice()); end rule "无等候费" salience 10 //指定优先级,数值越大优先级越高,不指定的情况下由上到下执行 no-loop true //防止陷入死循环 when /*规则条件,到工作内存中查找FeeRuleRequest对象 里面出来的结果只能是ture或者false $rule是绑定变量名,可以任意命名,官方推荐$符号,定义了绑定变量名,可以在then部分操作fact对象*/ $rule:FeeRuleRequest(waitMinute <= 10) then feeRuleResponse.setBaseWaitMinute(10); feeRuleResponse.setExceedWaitMinute(0); feeRuleResponse.setExceedWaitMinutePrice(new BigDecimal("1.0")); System.out.println("等候费:无"); end /** 4.远途费 订单行程超出12公里后每公里1元 */ rule "远途费 订单行程超出12公里后每公里1元" salience 10 //指定优先级,数值越大优先级越高,不指定的情况下由上到下执行 no-loop true //防止陷入死循环 when /*规则条件,到工作内存中查找FeeRuleRequest对象 里面出来的结果只能是ture或者false $rule是绑定变量名,可以任意命名,官方推荐$符号,定义了绑定变量名,可以在then部分操作fact对象*/ $rule:FeeRuleRequest(distance.doubleValue() > 12.0) then BigDecimal exceedLongDistance = $rule.getDistance().subtract(new BigDecimal("12.0")); feeRuleResponse.setBaseLongDistance(new BigDecimal("12.0")); feeRuleResponse.setExceedLongDistance(exceedLongDistance); feeRuleResponse.setExceedLongDistancePrice(new BigDecimal("1.0")); System.out.println("远途费,超出公里:" + feeRuleResponse.getExceedLongDistance() + "公里,单价:" + feeRuleResponse.getExceedLongDistancePrice()); end rule "无远途费" salience 10 //指定优先级,数值越大优先级越高,不指定的情况下由上到下执行 no-loop true //防止陷入死循环 when /*规则条件,到工作内存中查找FeeRuleRequest对象 里面出来的结果只能是ture或者false $rule是绑定变量名,可以任意命名,官方推荐$符号,定义了绑定变量名,可以在then部分操作fact对象*/ $rule:FeeRuleRequest(distance.doubleValue() <= 12.0) then feeRuleResponse.setBaseLongDistance(new BigDecimal("12.0")); feeRuleResponse.setExceedLongDistance(new BigDecimal("0")); feeRuleResponse.setExceedLongDistancePrice(new BigDecimal("0")); System.out.println("远途费:无"); end /** 5.计算总金额 订单总金额 = 基础里程费 + 超出基础里程的费 + 等候费 + 远程费 */ rule "计算总金额" salience 10 //指定优先级,数值越大优先级越高,不指定的情况下由上到下执行 no-loop true //防止陷入死循环 when /*规则条件,到工作内存中查找FeeRuleRequest对象 里面出来的结果只能是ture或者false $rule是绑定变量名,可以任意命名,官方推荐$符号,定义了绑定变量名,可以在then部分操作fact对象*/ $rule:FeeRuleRequest(distance.doubleValue() > 0.0) then //订单总金额 = 基础里程费 + 超出基础里程的费 + 等候费 + 远程费 BigDecimal distanceFee = feeRuleResponse.getBaseDistanceFee().add(feeRuleResponse.getExceedDistance().multiply(feeRuleResponse.getExceedDistancePrice())); BigDecimal waitFee = new BigDecimal(feeRuleResponse.getExceedWaitMinute()).multiply(feeRuleResponse.getExceedWaitMinutePrice()); BigDecimal longDistanceFee = feeRuleResponse.getExceedLongDistance().multiply(feeRuleResponse.getExceedLongDistancePrice()); BigDecimal totalAmount = distanceFee.add(waitFee).add(longDistanceFee); feeRuleResponse.setDistanceFee(distanceFee); feeRuleResponse.setWaitFee(waitFee); feeRuleResponse.setLongDistanceFee(longDistanceFee); feeRuleResponse.setTotalAmount(totalAmount); System.out.println("计算总金额:" + feeRuleResponse.getTotalAmount() + "元"); end
预估订单金额接口
FeeRuleController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Slf4j @RestController @RequestMapping("/rules/fee") @SuppressWarnings({"unchecked", "rawtypes"}) public class FeeRuleController { @Autowired private FeeRuleService feeRuleService; @Operation(summary = "计算订单费用") @PostMapping("/calculateOrderFee") public Result<FeeRuleResponseVo> calculateOrderFee (@RequestBody FeeRuleRequestForm calculateOrderFeeForm) { FeeRuleResponseVo feeRuleResponseVo = feeRuleService.calculateOrderFee(calculateOrderFeeForm); return Result.ok(feeRuleResponseVo); } }
FeeRuleService
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 @Slf4j @Service @SuppressWarnings({"unchecked", "rawtypes"}) public class FeeRuleServiceImpl implements FeeRuleService { @Autowired private KieContainer kieContainer; @Override public FeeRuleResponseVo calculateOrderFee (FeeRuleRequestForm form) { FeeRuleRequest request = new FeeRuleRequest (); request.setDistance(form.getDistance()); Date startTime = form.getStartTime(); request.setStartTime(new DateTime (startTime).toString("HH:mm:ss" )); request.setWaitMinute(form.getWaitMinute()); KieSession session = kieContainer.newKieSession(); FeeRuleResponseVo response = new FeeRuleResponseVo (); session.setGlobal("feeRuleResponse" ,response); session.insert(request); session.fireAllRules(); session.dispose(); FeeRuleResponseVo vo = new FeeRuleResponseVo (); BeanUtils.copyProperties(response,vo); return vo; } }
远程调用接口 1 2 3 4 5 6 7 8 9 10 11 @FeignClient(value = "service-rules") public interface FeeRuleFeignClient { @PostMapping("/rules/fee/calculateOrderFee") Result<FeeRuleResponseVo> calculateOrderFee (@RequestBody FeeRuleRequestForm calculateOrderFeeForm) ; }
预估订单数据接口
OrderController
1 2 3 4 5 6 7 8 9 @Autowired private OrderService orderService; @Operation(summary = "预估订单数据") @LoginDetection @PostMapping("/expectOrder") public Result<ExpectOrderVo> expectOrder (@RequestBody ExpectOrderForm expectOrderForm) { return Result.ok(orderService.expectOrder(expectOrderForm)); }
OrderService
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 @Slf4j @Service @SuppressWarnings({"unchecked", "rawtypes"}) public class OrderServiceImpl implements OrderService { @Autowired private MapFeignClient mapFeignClient; @Autowired private FeeRuleFeignClient feeRuleFeignClient; @Override public ExpectOrderVo expectOrder (ExpectOrderForm expectOrderForm) { CalculateDrivingLineForm calculateDrivingLineForm = new CalculateDrivingLineForm (); BeanUtils.copyProperties(expectOrderForm,calculateDrivingLineForm); Result<DrivingLineVo> drivingLineVoResult = mapFeignClient.calculateDrivingLine(calculateDrivingLineForm); DrivingLineVo drivingLineVo = drivingLineVoResult.getData(); FeeRuleRequestForm calculateOrderFeeForm = new FeeRuleRequestForm (); calculateOrderFeeForm.setDistance(drivingLineVo.getDistance()); calculateOrderFeeForm.setStartTime(new Date ()); calculateOrderFeeForm.setWaitMinute(0 ); Result<FeeRuleResponseVo> feeRuleResponseVoResult = feeRuleFeignClient.calculateOrderFee(calculateOrderFeeForm); FeeRuleResponseVo feeRuleResponseVo = feeRuleResponseVoResult.getData(); ExpectOrderVo expectOrderVo = new ExpectOrderVo (); expectOrderVo.setDrivingLineVo(drivingLineVo); expectOrderVo.setFeeRuleResponseVo(feeRuleResponseVo); return expectOrderVo; } }
乘客下单(一) 点击 呼叫代驾 会生成代驾订单,在 order_info 里添加订单数据
乘客下单接口
controller 不写了,就是调用获取数据
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 @Service @SuppressWarnings({"unchecked", "rawtypes"}) public class OrderInfoServiceImpl extends ServiceImpl <OrderInfoMapper, OrderInfo> implements OrderInfoService { @Autowired private OrderInfoMapper orderInfoMapper; @Autowired private OrderStatusLogMapper orderStatusLogMapper; @Override public Long saveOrderInfo (OrderInfoForm orderInfoForm) { OrderInfo orderInfo = new OrderInfo (); BeanUtils.copyProperties(orderInfoForm, orderInfo); orderInfo.setOrderNo(UUID.randomUUID().toString().replaceAll("-" , "" )); orderInfo.setStatus(OrderStatus.WAITING_ACCEPT.getStatus()); orderInfoMapper.insert(orderInfo); this .log(orderInfo.getId(), orderInfo.getStatus()); return orderInfo.getId(); } public void log (Long orderId, Integer status) { OrderStatusLog orderStatusLog = new OrderStatusLog (); orderStatusLog.setOrderId(orderId); orderStatusLog.setOrderStatus(status); orderStatusLog.setOperateTime(new Date ()); orderStatusLogMapper.insert(orderStatusLog); } }
远程调用
1 2 3 4 5 6 7 8 9 10 11 @FeignClient(value = "service-order") public interface OrderInfoFeignClient { @PostMapping("/order/info/saveOrderInfo") Result<Long> saveOrderInfo (@RequestBody OrderInfoForm orderInfoForm) ; }
web-service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 @Override public Long submitOrder (SubmitOrderForm submitOrderForm) { CalculateDrivingLineForm calculateDrivingLineForm = new CalculateDrivingLineForm (); BeanUtils.copyProperties(submitOrderForm,submitOrderForm); Result<DrivingLineVo> drivingLineVoResult = mapFeignClient.calculateDrivingLine(calculateDrivingLineForm); DrivingLineVo drivingLineVo = drivingLineVoResult.getData(); FeeRuleRequestForm calculateOrderFeeForm = new FeeRuleRequestForm (); calculateOrderFeeForm.setDistance(drivingLineVo.getDistance()); calculateOrderFeeForm.setStartTime(new Date ()); calculateOrderFeeForm.setWaitMinute(0 ); Result<FeeRuleResponseVo> feeRuleResponseVoResult = feeRuleFeignClient.calculateOrderFee(calculateOrderFeeForm); FeeRuleResponseVo feeRuleResponseVo = feeRuleResponseVoResult.getData(); OrderInfoForm orderInfoForm = new OrderInfoForm (); BeanUtils.copyProperties(submitOrderForm,orderInfoForm); orderInfoForm.setExpectDistance(drivingLineVo.getDistance()); orderInfoForm.setExpectAmount(feeRuleResponseVo.getTotalAmount()); Result<Long> orderInfoResult = orderInfoFeignClient.saveOrderInfo(orderInfoForm); Long orderId = orderInfoResult.getData(); return orderId; }
查询订单状态 订单微服务模块接口
根据订单id得到订单状态
OrderInfoController
1 2 3 4 5 @Operation(summary = "根据订单id获取订单状态") @GetMapping("/getOrderStatus/{orderId}") public Result<Integer> getOrderStatus (@PathVariable Long orderId) { return Result.ok(orderInfoService.getOrderStatus(orderId)); }
service
1 2 3 4 5 6 7 8 9 10 11 12 13 @Override public Integer getOrderStatus (Long orderId) { LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper <>(); wrapper.eq(OrderInfo::getId, orderId); wrapper.select(OrderInfo::getStatus); OrderInfo orderInfo = orderInfoMapper.selectOne(wrapper); if (orderInfo == null ){ return OrderStatus.NULL_ORDER.getStatus(); } return orderInfo.getStatus(); }
远程调用
1 2 3 4 5 6 7 @GetMapping("/order/info/getOrderStatus/{orderId}") Result<Integer> getOrderStatus (@PathVariable("orderId") Long orderId) ;
远程调用 乘客端 web-customer controller
1 2 3 4 5 6 @Operation(summary = "查询订单状态") @LoginDetection @GetMapping("/getOrderStatus/{orderId}") public Result<Integer> getOrderStatus (@PathVariable Long orderId) { return Result.ok(orderService.getOrderStatus(orderId)); }
service
1 2 3 4 5 @Override public Integer getOrderStatus (Long orderId) { Result<Integer> integerResult = orderInfoFeignClient.getOrderStatus(orderId); return integerResult.getData(); }
司机端 web-driver controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Tag(name = "订单API接口管理") @RestController @RequestMapping("/order") @SuppressWarnings({"unchecked", "rawtypes"}) public class OrderController { @Autowired private OrderService orderService; @Operation(summary = "查询订单状态") @GuiguLogin @GetMapping("/getOrderStatus/{orderId}") public Result<Integer> getOrderStatus (@PathVariable Long orderId) { return Result.ok(orderService.getOrderStatus(orderId)); } }
service
1 2 3 4 5 6 7 8 9 10 11 12 @Service @SuppressWarnings({"unchecked", "rawtypes"}) public class OrderServiceImpl implements OrderService { @Autowired private OrderInfoFeignClient orderInfoFeignClient; @Override public Integer getOrderStatus (Long orderId) { return orderInfoFeignClient.getOrderStatus(orderId).getData(); } }
乘客下单(二) 搜索附件可以下单的司机 Redis的GEO功能
1 GEOADD zhangsan 116.403963 39.915119 tiananmen 116.417876 39.915411 wangfujing 116.404354 39.904748 qianmen
1 GEORADIUS zhangsan 116.4000 39.9000 1 km WITHDIST
实时更新司机的位置信息 封装位置相关接口 service.service-map
controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @Tag(name = "位置API接口管理") @RestController @RequestMapping("/map/location") @SuppressWarnings({"unchecked", "rawtypes"}) public class LocationController { @Autowired private LocationService locationService; @Operation(summary = "开启接单服务:更新司机经纬度位置") @PostMapping("/updateDriverLocation") public Result<Boolean> updateDriverLocation (@RequestBody UpdateDriverLocationForm updateDriverLocationForm) { Boolean flag = locationService.updateDriverLocation(updateDriverLocationForm); return Result.ok(flag); } @Operation(summary = "关闭接单服务:删除司机经纬度位置") @DeleteMapping("/removeDriverLocation/{driverId}") public Result<Boolean> removeDriverLocation (@PathVariable Long driverId) { return Result.ok(locationService.removeDriverLocation(driverId)); } }
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 @Slf4j @Service @SuppressWarnings({"unchecked", "rawtypes"}) public class LocationServiceImpl implements LocationService { @Autowired private RedisTemplate redisTemplate; @Override public Boolean updateDriverLocation (UpdateDriverLocationForm updateDriverLocationForm) { Point point = new Point (updateDriverLocationForm.getLongitude().doubleValue(), updateDriverLocationForm.getLatitude().doubleValue()); redisTemplate.opsForGeo().add(RedisConstant.DRIVER_GEO_LOCATION, point, updateDriverLocationForm.getDriverId().toString()); return true ; } @Override public Boolean removeDriverLocation (Long driverId) { redisTemplate.opsForGeo().remove(RedisConstant.DRIVER_GEO_LOCATION,driverId.toString()); return true ; } }
远程调用定义 service-client.service-map-client
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @FeignClient(value = "service-map") public interface LocationFeignClient { @PostMapping("/map/location/updateDriverLocation") Result<Boolean> updateDriverLocation (@RequestBody UpdateDriverLocationForm updateDriverLocationForm) ; @DeleteMapping("/map/location/removeDriverLocation/{driverId}") Result<Boolean> removeDriverLocation (@PathVariable("driverId") Long driverId) ; }
司机web端 web.web-driver.controller
controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Slf4j @Tag(name = "位置API接口管理") @RestController @RequestMapping(value="/location") @SuppressWarnings({"unchecked", "rawtypes"}) public class LocationController { @Autowired private LocationService locationService; @Operation(summary = "开启接单服务:更新司机经纬度位置") @LoginDetection @PostMapping("/updateDriverLocation") public Result<Boolean> updateDriverLocation (@RequestBody UpdateDriverLocationForm updateDriverLocationForm) { Long driverId = AuthContextHolder.getUserId(); updateDriverLocationForm.setDriverId(driverId); return Result.ok(locationService.updateDriverLocation(updateDriverLocationForm)); } }
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Slf4j @Service @SuppressWarnings({"unchecked", "rawtypes"}) public class LocationServiceImpl implements LocationService { @Autowired private LocationFeignClient locationFeignClient; @Override public Boolean updateDriverLocation (UpdateDriverLocationForm updateDriverLocationForm) { Result<Boolean> booleanResult = locationFeignClient.updateDriverLocation(updateDriverLocationForm); return booleanResult.getData(); } }
获取司机个性化设置信息 封装查询司机个性化信息接口 service.service-driver.driver
controller
1 2 3 4 5 @Operation(summary = "获取司机设置信息") @GetMapping("/getDriverSet/{driverId}") public Result<DriverSet> getDriverSet (@PathVariable Long driverId) { return Result.ok(driverInfoService.getDriverSet(driverId)); }
service
1 2 3 4 5 6 7 @Override public DriverSet getDriverSet (Long driverId) { LambdaQueryWrapper<DriverSet> wrapper = new LambdaQueryWrapper <>(); wrapper.eq(DriverSet::getDriverId, driverId); DriverSet driverSet = driverSetMapper.selectOne(wrapper); return driverSet; }
远程调用 service-client.serivce-driver-client.DriverInfoFeignClient
1 2 @GetMapping("/driver/info/getDriverSet/{driverId}") Result<DriverSet> getDriverSet (@PathVariable("driverId") Long driverId) ;
修改司机web端 web.web-driver.controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 @Slf4j @Service @SuppressWarnings({"unchecked", "rawtypes"}) public class LocationServiceImpl implements LocationService { @Autowired private LocationFeignClient locationFeignClient; @Autowired private DriverInfoFeignClient driverInfoFeignClient; @Override public Boolean updateDriverLocation (UpdateDriverLocationForm updateDriverLocationForm) { Long driverId = updateDriverLocationForm.getDriverId(); Result<DriverSet> driverSet = driverInfoFeignClient.getDriverSet(driverId); DriverSet data = driverSet.getData(); if (data.getServiceStatus() == 1 ){ Result<Boolean> booleanResult = locationFeignClient.updateDriverLocation(updateDriverLocationForm); return booleanResult.getData(); }else { throw new GuiguException (ResultCodeEnum.NO_START_SERVICE); } } }
搜索附件适合接单的司机 封装搜索接单司机接口 service-map.map
controller
1 2 3 4 5 6 @Operation(summary = "搜索附近满足条件的司机") @PostMapping("/searchNearByDriver") public Result<List<NearByDriverVo>> searchNearByDriver (@RequestBody SearchNearByDriverForm searchNearByDriverForm) { return Result.ok(locationService.searchNearByDriver(searchNearByDriverForm)); }
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 @Override public List<NearByDriverVo> searchNearByDriver (SearchNearByDriverForm searchNearByDriverForm) { Point point = new Point (searchNearByDriverForm.getLongitude().doubleValue(), searchNearByDriverForm.getLatitude().doubleValue()); Distance distance = new Distance (SystemConstant.NEARBY_DRIVER_RADIUS, RedisGeoCommands.DistanceUnit.KILOMETERS); Circle circle = new Circle (point,distance); RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs() .includeDistance() .includeCoordinates() .sortAscending(); GeoResults<RedisGeoCommands.GeoLocation<String>> result = redisTemplate.opsForGeo().radius(RedisConstant.DRIVER_GEO_LOCATION, circle, args); List<GeoResult<RedisGeoCommands.GeoLocation<String>>> content = result.getContent(); List<NearByDriverVo> list = new ArrayList <>(); if (!CollectionUtils.isEmpty(content)) { Iterator<GeoResult<RedisGeoCommands.GeoLocation<String>>> iterator = content.iterator(); while (iterator.hasNext()) { GeoResult<RedisGeoCommands.GeoLocation<String>> item = iterator.next(); Long driverId = Long.parseLong(item.getContent().getName()); Result<DriverSet> driverSetResult = driverInfoFeignClient.getDriverSet(driverId); DriverSet driverSet = driverSetResult.getData(); BigDecimal orderDistance = driverSet.getOrderDistance(); if (orderDistance.doubleValue() != 0 && orderDistance.subtract(searchNearByDriverForm.getMileageDistance()).doubleValue()<0 ) { continue ; } BigDecimal currentDistance = new BigDecimal (item.getDistance().getValue()).setScale(2 , RoundingMode.HALF_UP); BigDecimal acceptDistance = driverSet.getAcceptDistance(); if (acceptDistance.doubleValue() !=0 && acceptDistance.subtract(currentDistance).doubleValue()<0 ) { continue ; } NearByDriverVo nearByDriverVo = new NearByDriverVo (); nearByDriverVo.setDriverId(driverId); nearByDriverVo.setDistance(currentDistance); list.add(nearByDriverVo); } } return list; }
接口测试
service-map 和 service-driver
http://localhost:8503/doc.html#/home
第一个接口测试用例及结果 ![[Pasted image 20250528233022.png]]
发现错误来源于redis,开始排查
排查成功,犯了两个很低级的错误
测试错误接口
nacos没有改配置文件,导致redis连接不上
再次测试为成功 ![[Pasted image 20250528233937.png]]
第二个接口测试用例及结果 ![[Pasted image 20250528234733.png]]
还是失败,挠头了,去看看报错是什么 ![[Pasted image 20250528235311.png]]
空对象?ok啊,结合自己聪明的大脑以及一点外界帮助,得出原因是因为司机只有一个,但是却有三满足条件司机的数据,程序蒙了,不知道返回哪个
解决方法是:因为是测试嘛,也不用太严谨,自己手动在数据库里面凭空产生点司机出来就行
再次测试,成功 ![[Pasted image 20250528235542.png]]
集成XXL-JOB service.serivce-dispatch模块
导入依赖 1 2 3 4 5 6 <dependencies > <dependency > <groupId > com.xuxueli</groupId > <artifactId > xxl-job-core</artifactId > </dependency > </dependencies >
执行器配置
编写任务job方法测试是否成功 1 2 3 4 5 6 7 8 @Component public class DispatchJobHandler { @XxlJob("firstJobHandler") public void testJobHandler () { System.out.println("xxl-job项目集成测试" ); } }
测试 第一步,启动相关服务
第二步,在调度中心创建任务
第三步,启动任务
测试的时候遇到点小问题:
先运行调度中心再运行项目
失败要留意xxl的错误日志,哪里有问题就删哪里
封装XXL-JOB客户端 调度中心创建任务方法 在xxl-job-admin中的controller中创建
JobInfoController把原来的添加删除修改任务替换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 @RequestMapping("/addJob") @ResponseBody @PermissionLimit(limit = false) public ReturnT<String> addJobInfo (@RequestBody XxlJobInfo jobInfo) { return xxlJobService.add(jobInfo); } @RequestMapping("/removeJob") @ResponseBody @PermissionLimit(limit = false) public ReturnT<String> removeJob (@RequestBody XxlJobInfo jobInfo) { return xxlJobService.remove(jobInfo.getId()); } @RequestMapping("/updateJob") @ResponseBody @PermissionLimit(limit = false) public ReturnT<String> updateJob (@RequestBody XxlJobInfo jobInfo) { return xxlJobService.update(jobInfo); } @RequestMapping("/stopJob") @ResponseBody @PermissionLimit(limit = false) public ReturnT<String> pauseJob (@RequestBody XxlJobInfo jobInfo) { return xxlJobService.stop(jobInfo.getId()); } @RequestMapping("/startJob") @ResponseBody @PermissionLimit(limit = false) public ReturnT<String> startJob (@RequestBody XxlJobInfo jobInfo) { return xxlJobService.start(jobInfo.getId()); } @RequestMapping("/addAndStartJob") @ResponseBody @PermissionLimit(limit = false) public ReturnT<String> addAndStartJob (@RequestBody XxlJobInfo jobInfo) { ReturnT<String> result = xxlJobService.add(jobInfo); String content = result.getContent(); int id = Integer.parseInt(content); xxlJobService.start(id); JobTriggerPoolHelper.trigger(id, TriggerTypeEnum.MANUAL, -1 , null , jobInfo.getExecutorParam(), "" ); return result; }
执行器项目配置文件添加任务方法
修改nacos配置文件 service-dispatch-dev.yaml
在job.admin下1 2 3 4 5 6 7 client: jobGroupId: 1 addUrl: ${xxl.job.admin.addresses}/jobinfo/addJob removeUrl: ${xxl.job.admin.addresses}/jobinfo/removeJob startJobUrl: ${xxl.job.admin.addresses}/jobinfo/startJob stopJobUrl: ${xxl.job.admin.addresses}/jobinfo/stopJob addAndStartUrl: ${xxl.job.admin.addresses}/jobinfo/addAndStartJob
添加相关配置类service-dispatch
创建配置类,读取配置文件里面的调用的调度中心的操作方法1 2 3 4 5 6 7 8 9 10 11 12 @Data @Component @ConfigurationProperties(prefix = "xxl.job.client") public class XxlJobClientConfig { private Integer jobGroupId; private String addUrl; private String removeUrl; private String startJobUrl; private String stopJobUrl; private String addAndStartUrl; }
创建客户端类,编写调用调度中心里面的方法1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 import com.alibaba.fastjson.JSONObject;import com.atguigu.daijia.common.execption.GuiguException;import com.atguigu.daijia.common.result.ResultCodeEnum;import com.atguigu.daijia.dispatch.xxl.config.XxlJobClientConfig;import com.atguigu.daijia.model.entity.dispatch.XxlJobInfo;import lombok.SneakyThrows;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.http.HttpEntity;import org.springframework.http.HttpHeaders;import org.springframework.http.MediaType;import org.springframework.http.ResponseEntity;import org.springframework.stereotype.Component;import org.springframework.web.client.RestTemplate;@Slf4j @Component public class XxlJobClient { @Autowired private XxlJobClientConfig xxlJobClientConfig; @Autowired private RestTemplate restTemplate; @SneakyThrows public Long addJob (String executorHandler, String param, String corn, String desc) { XxlJobInfo xxlJobInfo = new XxlJobInfo (); xxlJobInfo.setJobGroup(xxlJobClientConfig.getJobGroupId()); xxlJobInfo.setJobDesc(desc); xxlJobInfo.setAuthor("qy" ); xxlJobInfo.setScheduleType("CRON" ); xxlJobInfo.setScheduleConf(corn); xxlJobInfo.setGlueType("BEAN" ); xxlJobInfo.setExecutorHandler(executorHandler); xxlJobInfo.setExecutorParam(param); xxlJobInfo.setExecutorRouteStrategy("FIRST" ); xxlJobInfo.setExecutorBlockStrategy("SERIAL_EXECUTION" ); xxlJobInfo.setMisfireStrategy("FIRE_ONCE_NOW" ); xxlJobInfo.setExecutorTimeout(0 ); xxlJobInfo.setExecutorFailRetryCount(0 ); HttpHeaders headers = new HttpHeaders (); headers.setContentType(MediaType.APPLICATION_JSON); HttpEntity<XxlJobInfo> request = new HttpEntity <>(xxlJobInfo, headers); String url = xxlJobClientConfig.getAddUrl(); ResponseEntity<JSONObject> response = restTemplate.postForEntity(url, request, JSONObject.class); if (response.getStatusCode().value() == 200 && response.getBody().getIntValue("code" ) == 200 ) { log.info("增加xxl执行任务成功,返回信息:{}" , response.getBody().toJSONString()); return response.getBody().getLong("content" ); } log.info("调用xxl增加执行任务失败:{}" , response.getBody().toJSONString()); throw new GuiguException (ResultCodeEnum.DATA_ERROR); } public Boolean startJob (Long jobId) { XxlJobInfo xxlJobInfo = new XxlJobInfo (); xxlJobInfo.setId(jobId.intValue()); HttpHeaders headers = new HttpHeaders (); headers.setContentType(MediaType.APPLICATION_JSON); HttpEntity<XxlJobInfo> request = new HttpEntity <>(xxlJobInfo, headers); String url = xxlJobClientConfig.getStartJobUrl(); ResponseEntity<JSONObject> response = restTemplate.postForEntity(url, request, JSONObject.class); if (response.getStatusCode().value() == 200 && response.getBody().getIntValue("code" ) == 200 ) { log.info("启动xxl执行任务成功:{},返回信息:{}" , jobId, response.getBody().toJSONString()); return true ; } log.info("启动xxl执行任务失败:{},返回信息:{}" , jobId, response.getBody().toJSONString()); throw new GuiguException (ResultCodeEnum.DATA_ERROR); } public Boolean stopJob (Long jobId) { XxlJobInfo xxlJobInfo = new XxlJobInfo (); xxlJobInfo.setId(jobId.intValue()); HttpHeaders headers = new HttpHeaders (); headers.setContentType(MediaType.APPLICATION_JSON); HttpEntity<XxlJobInfo> request = new HttpEntity <>(xxlJobInfo, headers); String url = xxlJobClientConfig.getStopJobUrl(); ResponseEntity<JSONObject> response = restTemplate.postForEntity(url, request, JSONObject.class); if (response.getStatusCode().value() == 200 && response.getBody().getIntValue("code" ) == 200 ) { log.info("停止xxl执行任务成功:{},返回信息:{}" , jobId, response.getBody().toJSONString()); return true ; } log.info("停止xxl执行任务失败:{},返回信息:{}" , jobId, response.getBody().toJSONString()); throw new GuiguException (ResultCodeEnum.DATA_ERROR); } public Boolean removeJob (Long jobId) { XxlJobInfo xxlJobInfo = new XxlJobInfo (); xxlJobInfo.setId(jobId.intValue()); HttpHeaders headers = new HttpHeaders (); headers.setContentType(MediaType.APPLICATION_JSON); HttpEntity<XxlJobInfo> request = new HttpEntity <>(xxlJobInfo, headers); String url = xxlJobClientConfig.getRemoveUrl(); ResponseEntity<JSONObject> response = restTemplate.postForEntity(url, request, JSONObject.class); if (response.getStatusCode().value() == 200 && response.getBody().getIntValue("code" ) == 200 ) { log.info("删除xxl执行任务成功:{},返回信息:{}" , jobId, response.getBody().toJSONString()); return true ; } log.info("删除xxl执行任务失败:{},返回信息:{}" , jobId, response.getBody().toJSONString()); throw new GuiguException (ResultCodeEnum.DATA_ERROR); } public Long addAndStart (String executorHandler, String param, String corn, String desc) { XxlJobInfo xxlJobInfo = new XxlJobInfo (); xxlJobInfo.setJobGroup(xxlJobClientConfig.getJobGroupId()); xxlJobInfo.setJobDesc(desc); xxlJobInfo.setAuthor("qy" ); xxlJobInfo.setScheduleType("CRON" ); xxlJobInfo.setScheduleConf(corn); xxlJobInfo.setGlueType("BEAN" ); xxlJobInfo.setExecutorHandler(executorHandler); xxlJobInfo.setExecutorParam(param); xxlJobInfo.setExecutorRouteStrategy("FIRST" ); xxlJobInfo.setExecutorBlockStrategy("SERIAL_EXECUTION" ); xxlJobInfo.setMisfireStrategy("FIRE_ONCE_NOW" ); xxlJobInfo.setExecutorTimeout(0 ); xxlJobInfo.setExecutorFailRetryCount(0 ); HttpHeaders headers = new HttpHeaders (); headers.setContentType(MediaType.APPLICATION_JSON); HttpEntity<XxlJobInfo> request = new HttpEntity <>(xxlJobInfo, headers); String url = xxlJobClientConfig.getAddAndStartUrl(); ResponseEntity<JSONObject> response = restTemplate.postForEntity(url, request, JSONObject.class); if (response.getStatusCode().value() == 200 && response.getBody().getIntValue("code" ) == 200 ) { log.info("增加并开始执行xxl任务成功,返回信息:{}" , response.getBody().toJSONString()); return response.getBody().getLong("content" ); } log.info("增加并开始执行xxl任务失败:{}" , response.getBody().toJSONString()); throw new GuiguException (ResultCodeEnum.DATA_ERROR); } }
启动类配置RestTemplate 直接把RestTemplate写在启动类里
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @SpringBootApplication @EnableDiscoveryClient @EnableFeignClients public class ServiceDispatchApplication { public static void main (String[] args) { SpringApplication.run(ServiceDispatchApplication.class, args); } @Bean public RestTemplate restTemplate () { return new RestTemplate (); } }
创建并启动任务接口 service-dispatch
controller
1 2 3 4 5 6 7 @Operation(summary = "添加并开始新订单任务调度") @PostMapping("/addAndStartTask") public Result<Long> addAndStartTask (@RequestBody NewOrderTaskVo newOrderTaskVo) { Long id = newOrderService.addAndStartTask(newOrderTaskVo); return Result.ok(id); }
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 @Override public Long addAndStartTask (NewOrderTaskVo newOrderTaskVo) { LambdaQueryWrapper<OrderJob> wrapper = new LambdaQueryWrapper <>(); wrapper.eq(OrderJob::getOrderId, newOrderTaskVo.getOrderId()); OrderJob orderJob = orderJobMapper.selectOne(wrapper); if (orderJob == null ){ Long id = xxlJobClient.addAndStart("newOrderTaskHandler" , "" , "0 0/1 * * * ?" , "新创建订单任务调度:" + newOrderTaskVo.getOrderId()); orderJob = new OrderJob (); orderJob.setOrderId(newOrderTaskVo.getOrderId()); orderJob.setJobId(id); orderJob.setParameter(JSONObject.toJSONString(newOrderTaskVo)); orderJobMapper.insert(orderJob); } return orderJob.getJobId(); }
远程调用 service-client service-dispatch-client
1 2 3 4 5 6 7 8 9 10 11 @FeignClient(value = "service-dispatch") public interface NewOrderFeignClient { @PostMapping("/dispatch/newOrder/addAndStartTask") Result<Long> addAndStartTask (@RequestBody NewOrderTaskVo newOrderDispatchVo) ; }
开发具体任务job方法 JobHandler
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public class JobHandler { @Autowired private XxlJobLogMapper xxlJobLogMapper; @Autowired private NewOrderService newOrderService; @XxlJob("newOrderTaskHandler") public void newOrderTaskHandler () { XxlJobLog xxlJobLog = new XxlJobLog (); xxlJobLog.setJobId(XxlJobHelper.getJobId()); long startTime = System.currentTimeMillis(); try { newOrderService.executeTask(XxlJobHelper.getJobId()); xxlJobLog.setStatus(1 ); } catch (Exception e) { xxlJobLog.setStatus(0 ); xxlJobLog.setError(e.getMessage()); e.printStackTrace(); } finally { long times = System.currentTimeMillis()- startTime; xxlJobLog.setTimes((int )times); xxlJobLogMapper.insert(xxlJobLog); } } }
远程调用 service-driver-client LocationFeignClient1 2 3 @PostMapping("/map/location/searchNearByDriver") public Result<List<NearByDriverVo>> searchNearByDriver (@RequestBody SearchNearByDriverForm searchNearByDriverForm) ;
service-dispatch NewOrderServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 @Autowired private LocationFeignClient locationFeignClient;@Autowired private OrderInfoFeignClient orderInfoFeignClient;@Autowired private RedisTemplate redisTemplate;@Override public void executeTask (long jobId) { LambdaQueryWrapper<OrderJob> wrapper = new LambdaQueryWrapper <>(); wrapper.eq(OrderJob::getJobId, jobId); OrderJob orderJob = orderJobMapper.selectOne(wrapper); if (orderJob == null ){ return ; } String parameter = orderJob.getParameter(); NewOrderTaskVo newOrderTaskVo = JSONObject.parseObject(parameter, NewOrderTaskVo.class); Integer status = orderInfoFeignClient.getOrderStatus(newOrderTaskVo.getOrderId()).getData(); if (status.intValue() != OrderStatus.WAITING_ACCEPT.getStatus().intValue()){ xxlJobClient.stopJob(jobId); return ; } SearchNearByDriverForm from = new SearchNearByDriverForm (); SearchNearByDriverForm searchNearByDriverForm = new SearchNearByDriverForm (); searchNearByDriverForm.setLongitude(newOrderTaskVo.getStartPointLongitude()); searchNearByDriverForm.setLatitude(newOrderTaskVo.getStartPointLatitude()); List<NearByDriverVo> result = locationFeignClient.searchNearByDriver(from).getData(); result.forEach(driver -> { String repeatKey = RedisConstant.DRIVER_ORDER_REPEAT_LIST+newOrderTaskVo.getOrderId(); Boolean isMember = redisTemplate.opsForSet().isMember(repeatKey, driver.getDriverId()); if (!isMember){ redisTemplate.opsForSet().add(parameter, driver.getDriverId()); redisTemplate.expire(repeatKey, RedisConstant.DRIVER_ORDER_REPEAT_LIST_EXPIRES_TIME, TimeUnit.MINUTES); NewOrderDataVo newOrderDataVo = new NewOrderDataVo (); newOrderDataVo.setOrderId(newOrderTaskVo.getOrderId()); newOrderDataVo.setStartLocation(newOrderTaskVo.getStartLocation()); newOrderDataVo.setEndLocation(newOrderTaskVo.getEndLocation()); newOrderDataVo.setExpectAmount(newOrderTaskVo.getExpectAmount()); newOrderDataVo.setExpectDistance(newOrderTaskVo.getExpectDistance()); newOrderDataVo.setExpectTime(newOrderTaskVo.getExpectTime()); newOrderDataVo.setFavourFee(newOrderTaskVo.getFavourFee()); newOrderDataVo.setDistance(driver.getDistance()); newOrderDataVo.setCreateTime(newOrderTaskVo.getCreateTime()); String key = RedisConstant.DRIVER_ORDER_TEMP_LIST+driver.getDriverId(); redisTemplate.opsForList().leftPush(key, JSONObject.toJSONString(newOrderDataVo)); redisTemplate.expire(key, RedisConstant.DRIVER_ORDER_TEMP_LIST_EXPIRES_TIME, TimeUnit.MINUTES); } }); }
乘客下单添加任务调度 web-customer 补充之前OrderServiceImpl里写的查询附近可以接单司机
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 @Autowired private NewOrderFeignClient newOrderFeignClient;@Override public Long submitOrder (SubmitOrderForm submitOrderForm) { CalculateDrivingLineForm calculateDrivingLineForm = new CalculateDrivingLineForm (); BeanUtils.copyProperties(submitOrderForm,submitOrderForm); Result<DrivingLineVo> drivingLineVoResult = mapFeignClient.calculateDrivingLine(calculateDrivingLineForm); DrivingLineVo drivingLineVo = drivingLineVoResult.getData(); FeeRuleRequestForm calculateOrderFeeForm = new FeeRuleRequestForm (); calculateOrderFeeForm.setDistance(drivingLineVo.getDistance()); calculateOrderFeeForm.setStartTime(new Date ()); calculateOrderFeeForm.setWaitMinute(0 ); Result<FeeRuleResponseVo> feeRuleResponseVoResult = feeRuleFeignClient.calculateOrderFee(calculateOrderFeeForm); FeeRuleResponseVo feeRuleResponseVo = feeRuleResponseVoResult.getData(); OrderInfoForm orderInfoForm = new OrderInfoForm (); BeanUtils.copyProperties(submitOrderForm,orderInfoForm); orderInfoForm.setExpectDistance(drivingLineVo.getDistance()); orderInfoForm.setExpectAmount(feeRuleResponseVo.getTotalAmount()); Result<Long> orderInfoResult = orderInfoFeignClient.saveOrderInfo(orderInfoForm); Long orderId = orderInfoResult.getData(); NewOrderTaskVo newOrderTaskVo = new NewOrderTaskVo (); newOrderTaskVo.setOrderId(orderId); newOrderTaskVo.setStartLocation(orderInfoForm.getStartLocation()); newOrderTaskVo.setStartPointLongitude(orderInfoForm.getStartPointLongitude()); newOrderTaskVo.setStartPointLatitude(orderInfoForm.getStartPointLatitude()); newOrderTaskVo.setEndLocation(orderInfoForm.getEndLocation()); newOrderTaskVo.setEndPointLongitude(orderInfoForm.getEndPointLongitude()); newOrderTaskVo.setEndPointLatitude(orderInfoForm.getEndPointLatitude()); newOrderTaskVo.setExpectAmount(orderInfoForm.getExpectAmount()); newOrderTaskVo.setExpectDistance(orderInfoForm.getExpectDistance()); newOrderTaskVo.setExpectTime(drivingLineVo.getDuration()); newOrderTaskVo.setFavourFee(orderInfoForm.getFavourFee()); newOrderTaskVo.setCreateTime(new Date ()); Long jobId = newOrderFeignClient.addAndStartTask(newOrderTaskVo).getData(); return orderId; }
司机获取最新订单数据 service-dispatch
查询最新订单和清空司机队列数据 NewOrderController
1 2 3 4 5 6 7 8 9 10 11 @Operation(summary = "查询司机新订单数据") @GetMapping("/findNewOrderQueueData/{driverId}") public Result<List<NewOrderDataVo>> findNewOrderQueueData (@PathVariable Long driverId) { return Result.ok(newOrderService.findNewOrderQueueData(driverId)); } @Operation(summary = "清空新订单队列数据") @GetMapping("/clearNewOrderQueueData/{driverId}") public Result<Boolean> clearNewOrderQueueData (@PathVariable Long driverId) { return Result.ok(newOrderService.clearNewOrderQueueData(driverId)); }
NewOrderServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Override public List<NewOrderDataVo> findNewOrderQueueData (Long driverId) { List<NewOrderDataVo> list = new ArrayList <>(); String key = RedisConstant.DRIVER_ORDER_TEMP_LIST + driverId; Long size = redisTemplate.opsForList().size(key); if (size > 0 ) { for (int i = 0 ; i < size; i++) { String content = (String)redisTemplate.opsForList().leftPop(key); NewOrderDataVo newOrderDataVo = JSONObject.parseObject(content,NewOrderDataVo.class); list.add(newOrderDataVo); } } return list; } @Override public Boolean clearNewOrderQueueData (Long driverId) { String key = RedisConstant.DRIVER_ORDER_TEMP_LIST + driverId; redisTemplate.delete(key); return true ; }
远程调用 service-dispatch
NewOrderFeignClient
1 2 3 4 5 6 7 @GetMapping("/dispatch/newOrder/findNewOrderQueueData/{driverId}") Result<List<NewOrderDataVo>> findNewOrderQueueData (@PathVariable("driverId") Long driverId) ; @GetMapping("/dispatch/newOrder/clearNewOrderQueueData/{driverId}") Result<Boolean> clearNewOrderQueueData (@PathVariable("driverId") Long driverId) ;
司机web端调用 OrderController
1 2 3 4 5 6 7 @Operation(summary = "查询司机新订单数据") @LoginDetection @GetMapping("/findNewOrderQueueData") public Result<List<NewOrderDataVo>> findNewOrderQueueData () { Long driverId = AuthContextHolder.getUserId(); return Result.ok(orderService.findNewOrderQueueData(driverId)); }
service
1 2 3 4 5 6 7 @Autowired private NewOrderFeignClient newOrderFeignClient;@Override public List<NewOrderDataVo> findNewOrderQueueData (Long driverId) { return newOrderFeignClient.findNewOrderQueueData(driverId).getData(); }
司机接单 需求
乘客下单后,新订单信息已经放在司机临时队列,然后可以开始接单了
首先,司机登录、认证(身份证、驾驶证、创建人脸模型) 第二,司机每天接单前都需要人脸识别 第三,司机开始接单,更新接单状态 第四,司机接单后,删除司机之前存储在redis里面的位置信息 第五,司机接单后,清空临时队列新订单信息
查找司机端当前订单 设定是接单去需要验证司机是否有未完成的订单,暂时不管
1 2 3 4 5 6 7 8 9 @Operation(summary = "查找司机端当前订单") @LoginDetection @GetMapping("/searchDriverCurrentOrder") public Result<CurrentOrderInfoVo> searchDriverCurrentOrder () { CurrentOrderInfoVo currentOrderInfoVo = new CurrentOrderInfoVo (); currentOrderInfoVo.setIsHasCurrentOrder(false ); return Result.ok(currentOrderInfoVo); }
判断司机在当日是否人脸识别 service-driver DriverInfoController
1 2 3 4 5 @Operation(summary = "判断司机当日是否进行过人脸识别") @GetMapping("/isFaceRecognition/{driverId}") Result<Boolean> isFaceRecognition (@PathVariable("driverId") Long driverId) { return Result.ok(driverInfoService.isFaceRecognition(driverId)); }
service
1 2 3 4 5 6 7 8 9 10 11 12 13 @Override public Boolean isFaceRecognition (Long driverId) { LambdaQueryWrapper<DriverFaceRecognition> wrapper = new LambdaQueryWrapper <>(); wrapper.eq(DriverFaceRecognition::getDriverId,driverId); wrapper.eq(DriverFaceRecognition::getFaceDate,new DateTime ().toString("yyyy-MM-dd" )); Long count = driverFaceRecognitionMapper.selectCount(wrapper); return count != 0 ; }
远程调用
DriverInfoFeignClient
1 2 3 @GetMapping("/driver/info/isFaceRecognition/{driverId}") Result<Boolean> isFaceRecognition (@PathVariable("driverId") Long driverId) ;
web端调用 DriverController
1 2 3 4 5 6 7 @Operation(summary = "判断司机当日是否进行过人脸识别") @LoginDetection @GetMapping("/isFaceRecognition") Result<Boolean> isFaceRecognition () { Long driverId = AuthContextHolder.getUserId(); return Result.ok(driverService.isFaceRecognition(driverId)); }
service
1 2 3 4 5 6 @Override public Boolean isFaceRecognition (Long driverId) { Result<Boolean> faceRecognition = driverInfoFeignClient.isFaceRecognition(driverId); return faceRecognition.getData(); }
人脸识别接口
service-driver DriverInfoController
1 2 3 4 5 @Operation(summary = "验证司机人脸") @PostMapping("/verifyDriverFace") public Result<Boolean> verifyDriverFace (@RequestBody DriverFaceModelForm driverFaceModelForm) { return Result.ok(driverInfoService.verifyDriverFace(driverFaceModelForm)); }
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 @Override public Boolean verifyDriverFace (DriverFaceModelForm driverFaceModelForm) { try { Credential cred = new Credential (tencentCloudProperties.getSecretId(), tencentCloudProperties.getSecretKey()); HttpProfile httpProfile = new HttpProfile (); httpProfile.setEndpoint("iai.tencentcloudapi.com" ); ClientProfile clientProfile = new ClientProfile (); clientProfile.setHttpProfile(httpProfile); IaiClient client = new IaiClient (cred, tencentCloudProperties.getRegion(), clientProfile); VerifyFaceRequest req = new VerifyFaceRequest (); req.setImage(driverFaceModelForm.getImageBase64()); req.setPersonId(String.valueOf(driverFaceModelForm.getDriverId())); VerifyFaceResponse resp = client.VerifyFace(req); System.out.println(AbstractModel.toJsonString(resp)); if (resp.getIsMatch()){ Boolean isSuccess = this .detectLiveFace(driverFaceModelForm.getImageBase64()); if (isSuccess){ DriverFaceRecognition driverFaceRecognition = new DriverFaceRecognition (); driverFaceRecognition.setDriverId(driverFaceModelForm.getDriverId()); driverFaceRecognition.setFaceDate(new Date ()); driverFaceRecognitionMapper.insert(driverFaceRecognition); return true ; } } } catch (TencentCloudSDKException e) { System.out.println(e.toString()); } throw new GuiguException (ResultCodeEnum.DATA_ERROR); } private Boolean detectLiveFace (String imageBase64) { try { Credential cred = new Credential (tencentCloudProperties.getSecretId(), tencentCloudProperties.getSecretKey()); HttpProfile httpProfile = new HttpProfile (); httpProfile.setEndpoint("iai.tencentcloudapi.com" ); ClientProfile clientProfile = new ClientProfile (); clientProfile.setHttpProfile(httpProfile); IaiClient client = new IaiClient (cred, tencentCloudProperties.getRegion(), clientProfile); DetectLiveFaceRequest req = new DetectLiveFaceRequest (); req.setImage(imageBase64); DetectLiveFaceResponse resp = client.DetectLiveFace(req); System.out.println(DetectLiveFaceResponse.toJsonString(resp)); if (resp.getIsLiveness()) { return true ; } } catch (TencentCloudSDKException e) { System.out.println(e.toString()); } return false ; }
远程调用 service-driver-client DriverInfoFeignClient
1 2 3 @PostMapping("/driver/info/verifyDriverFace") Result<Boolean> verifyDriverFace (@RequestBody DriverFaceModelForm driverFaceModelForm) ;
web-driver DriverController
1 2 3 4 5 6 7 @Operation(summary = "验证司机人脸") @LoginDetection @PostMapping("/verifyDriverFace") public Result<Boolean> verifyDriverFace (@RequestBody DriverFaceModelForm driverFaceModelForm) { driverFaceModelForm.setDriverId(AuthContextHolder.getUserId()); return Result.ok(driverService.verifyDriverFace(driverFaceModelForm)); }
service
1 2 3 4 5 6 @Override public Boolean verifyDriverFace (DriverFaceModelForm driverFaceModelForm) { Result<Boolean> booleanResult = driverInfoFeignClient.verifyDriverFace(driverFaceModelForm); return booleanResult.getData(); }
更新司机接单状态 DriverInfoController
1 2 3 4 5 @Operation(summary = "更新接单状态") @GetMapping("/updateServiceStatus/{driverId}/{status}") public Result<Boolean> updateServiceStatus (@PathVariable Long driverId, @PathVariable Integer status) { return Result.ok(driverInfoService.updateServiceStatus(driverId, status)); }
service
1 2 3 4 5 6 7 8 9 @Override public Boolean updateServiceStatus (Long driverId, Integer status) { LambdaQueryWrapper<DriverSet> wrapper = new LambdaQueryWrapper <>(); wrapper.eq(DriverSet::getDriverId,driverId); DriverSet driverSet = new DriverSet (); driverSet.setServiceStatus(status); driverSetMapper.update(driverSet,wrapper); return true ; }
远程调用DriverInfoFeignClient
1 2 3 @GetMapping("/driver/info/updateServiceStatus/{driverId}/{status}") Result<Boolean> updateServiceStatus (@PathVariable("driverId") Long driverId, @PathVariable("status") Integer status) ;
开启接单服务web接口 在web-driver中进行编写
司机开启接单后,上传位置信息到redis的GEO,才能被任务调度搜索到司机信息,开始抢单
DriverController 1 2 3 4 5 6 7 @Operation(summary = "开始接单服务") @LoginDetection @GetMapping("/startService") public Result<Boolean> startService () { Long driverId = AuthContextHolder.getUserId(); return Result.ok(driverService.startService(driverId)); }
DriverServiceImpl 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 @Autowired private LocationFeignClient locationFeignClient; @Autowired private NewOrderFeignClient newOrderFeignClient;@Override public Boolean startService (Long driverId) { DriverLoginVo driverLoginVo = driverInfoFeignClient.getDriverLoginInfo(driverId).getData(); if (driverLoginVo.getAuthStatus()!=2 ) { throw new GuiguException (ResultCodeEnum.AUTH_ERROR); } Boolean isFace = driverInfoFeignClient.isFaceRecognition(driverId).getData(); if (!isFace) { throw new GuiguException (ResultCodeEnum.FACE_ERROR); } driverInfoFeignClient.updateServiceStatus(driverId,1 ); locationFeignClient.removeDriverLocation(driverId); newOrderFeignClient.clearNewOrderQueueData(driverId); return true ; }
停止接单服务web接口 DriverController
1 2 3 4 5 6 7 @Operation(summary = "停止接单服务") @LoginDetection @GetMapping("/stopService") public Result<Boolean> stopService () { Long driverId = AuthContextHolder.getUserId(); return Result.ok(driverService.stopService(driverId)); }
DriverServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 @Override public Boolean stopService (Long driverId) { driverInfoFeignClient.updateServiceStatus(driverId,0 ); locationFeignClient.removeDriverLocation(driverId); newOrderFeignClient.clearNewOrderQueueData(driverId); return true ; }
司机抢单 抢单接口 微服务抢单接口 service-order模块内
OrderInfoController
1 2 3 4 5 @Operation(summary = "司机抢单") @GetMapping("/robNewOrder/{driverId}/{orderId}") public Result<Boolean> robNewOrder (@PathVariable Long driverId, @PathVariable Long orderId) { return Result.ok(orderInfoService.robNewOrder(driverId, orderId)); }
在OrderInfoServiceImpl之前保存订单 方法修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 @Autowired private RedisTemplate redisTemplate;@Override public Long saveOrderInfo (OrderInfoForm orderInfoForm) { OrderInfo orderInfo = new OrderInfo (); BeanUtils.copyProperties(orderInfoForm,orderInfo); String orderNo = UUID.randomUUID().toString().replaceAll("-" ,"" ); orderInfo.setOrderNo(orderNo); orderInfo.setStatus(OrderStatus.WAITING_ACCEPT.getStatus()); orderInfoMapper.insert(orderInfo); this .log(orderInfo.getId(),orderInfo.getStatus()); redisTemplate.opsForValue().set(RedisConstant.ORDER_ACCEPT_MARK, "0" , RedisConstant.ORDER_ACCEPT_MARK_EXPIRES_TIME, TimeUnit.MINUTES); return orderInfo.getId(); }
OrderInfoServiceImpl新增司机抢单方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 @Override public Boolean robNewOrder (Long driverId, Long orderId) { if (!redisTemplate.hasKey(RedisConstant.ORDER_ACCEPT_MARK)) { throw new GuiguException (ResultCodeEnum.COB_NEW_ORDER_FAIL); } LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper <>(); wrapper.eq(OrderInfo::getId,orderId); OrderInfo orderInfo = orderInfoMapper.selectOne(wrapper); orderInfo.setStatus(OrderStatus.ACCEPTED.getStatus()); orderInfo.setStatus(OrderStatus.ACCEPTED.getStatus()); orderInfo.setDriverId(driverId); orderInfo.setAcceptTime(new Date ()); int rows = orderInfoMapper.updateById(orderInfo); if (rows != 1 ) { throw new GuiguException (ResultCodeEnum.COB_NEW_ORDER_FAIL); } redisTemplate.delete(RedisConstant.ORDER_ACCEPT_MARK); return true ; }
远程调用
1 2 3 4 5 6 7 8 @GetMapping("/order/info/robNewOrder/{driverId}/{orderId}") Result<Boolean> robNewOrder (@PathVariable("driverId") Long driverId, @PathVariable("orderId") Long orderId) ;
司机web端接口 OrderController
1 2 3 4 5 6 7 @Operation(summary = "司机抢单") @LoginDetection @GetMapping("/robNewOrder/{orderId}") public Result<Boolean> robNewOrder (@PathVariable Long orderId) { Long driverId = AuthContextHolder.getUserId(); return Result.ok(orderService.robNewOrder(driverId, orderId)); }
service
1 2 3 4 5 6 @Override public Boolean robNewOrder (Long driverId, Long orderId) { Result<Boolean> booleanResult = orderInfoFeignClient.robNewOrder(driverId, orderId); return booleanResult.getData(); }
添加Redisson分布式锁到司机抢单 在service里service-order中OrderInfoServiceImpl添加分布式锁
修改之前写的robNewOrder
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 @Autowired private RedissonClient redissonClient;@Override public Boolean robNewOrder (Long driverId, Long orderId) { if (!redisTemplate.hasKey(RedisConstant.ORDER_ACCEPT_MARK)) { throw new GuiguException (ResultCodeEnum.COB_NEW_ORDER_FAIL); } RLock lock = redissonClient.getLock(RedisConstant.ROB_NEW_ORDER_LOCK + orderId); try { boolean flag = lock.tryLock(RedisConstant.ROB_NEW_ORDER_LOCK_WAIT_TIME, RedisConstant.ROB_NEW_ORDER_LOCK_LEASE_TIME, TimeUnit.SECONDS); if (flag) { if (!redisTemplate.hasKey(RedisConstant.ORDER_ACCEPT_MARK)) { throw new GuiguException (ResultCodeEnum.COB_NEW_ORDER_FAIL); } LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper <>(); wrapper.eq(OrderInfo::getId,orderId); OrderInfo orderInfo = orderInfoMapper.selectOne(wrapper); orderInfo.setStatus(OrderStatus.ACCEPTED.getStatus()); orderInfo.setDriverId(driverId); orderInfo.setAcceptTime(new Date ()); int rows = orderInfoMapper.updateById(orderInfo); if (rows != 1 ) { throw new GuiguException (ResultCodeEnum.COB_NEW_ORDER_FAIL); } redisTemplate.delete(RedisConstant.ORDER_ACCEPT_MARK); } }catch (Exception e) { throw new GuiguException (ResultCodeEnum.COB_NEW_ORDER_FAIL); }finally { if (lock.isLocked()) { lock.unlock(); } } return true ; }
订单执行(一) 加载当前订单 需求
无论是司机端,还是乘客端,遇到页面切换,重新登录小程序等,只要回到首页面,查看当前是否有正在执行的订单,如果有跳转到当前订单的执行页面
乘客端查找当前订单 订单微服务接口
OrderInfoController1 2 3 4 5 @Operation(summary = "乘客端查找当前订单") @GetMapping("/searchCustomerCurrentOrder/{customerId}") public Result<CurrentOrderInfoVo> searchCustomerCurrentOrder (@PathVariable Long customerId) { return Result.ok(orderInfoService.searchCustomerCurrentOrder(customerId)); }
OrderInfoServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 @Autowired private RedissonClient redissonClient;@Override public CurrentOrderInfoVo searchCustomerCurrentOrder (Long customerId) { LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper <>(); wrapper.eq(OrderInfo::getCustomerId,customerId); Integer[] statusArray = { OrderStatus.ACCEPTED.getStatus(), OrderStatus.DRIVER_ARRIVED.getStatus(), OrderStatus.UPDATE_CART_INFO.getStatus(), OrderStatus.START_SERVICE.getStatus(), OrderStatus.END_SERVICE.getStatus(), OrderStatus.UNPAID.getStatus() }; wrapper.in(OrderInfo::getStatus,statusArray); wrapper.orderByDesc(OrderInfo::getId); wrapper.last(" limit 1" ); OrderInfo orderInfo = orderInfoMapper.selectOne(wrapper); CurrentOrderInfoVo currentOrderInfoVo = new CurrentOrderInfoVo (); if (orderInfo != null ) { currentOrderInfoVo.setOrderId(orderInfo.getId()); currentOrderInfoVo.setStatus(orderInfo.getStatus()); currentOrderInfoVo.setIsHasCurrentOrder(true ); } else { currentOrderInfoVo.setIsHasCurrentOrder(false ); } return currentOrderInfoVo; }
远程调用 serivce-order-client OrderInfoFeignClient
1 2 3 4 5 6 7 @GetMapping("/order/info/searchCustomerCurrentOrder/{customerId}") Result<CurrentOrderInfoVo> searchCustomerCurrentOrder (@PathVariable("customerId") Long customerId) ;
乘客web端接口 web-customer OrderController
1 2 3 4 5 6 7 @Operation(summary = "乘客端查找当前订单") @LoginDetection @GetMapping("/searchCustomerCurrentOrder") public Result<CurrentOrderInfoVo> searchCustomerCurrentOrder () { Long customerId = AuthContextHolder.getUserId(); return Result.ok(orderService.searchCustomerCurrentOrder(customerId)); }
OrderServiceImpl
1 2 3 4 5 6 @Override public CurrentOrderInfoVo searchCustomerCurrentOrder (Long customerId) { Result<CurrentOrderInfoVo> currentOrderInfoVoResult = orderInfoFeignClient.searchCustomerCurrentOrder(customerId); return currentOrderInfoVoResult.getData(); }
司机端查找当前订单 订单微服务接口 service-order OrderInfoController
1 2 3 4 5 @Operation(summary = "司机端查找当前订单") @GetMapping("/searchDriverCurrentOrder/{driverId}") public Result<CurrentOrderInfoVo> searchDriverCurrentOrder (@PathVariable Long driverId) { return Result.ok(orderInfoService.searchDriverCurrentOrder(driverId)); }
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 @Override public CurrentOrderInfoVo searchDriverCurrentOrder (Long driverId) { LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper <>(); wrapper.eq(OrderInfo::getDriverId,driverId); Integer[] statusArray = { OrderStatus.ACCEPTED.getStatus(), OrderStatus.DRIVER_ARRIVED.getStatus(), OrderStatus.UPDATE_CART_INFO.getStatus(), OrderStatus.START_SERVICE.getStatus(), OrderStatus.END_SERVICE.getStatus() }; wrapper.in(OrderInfo::getStatus,statusArray); wrapper.orderByDesc(OrderInfo::getId); wrapper.last(" limit 1" ); OrderInfo orderInfo = orderInfoMapper.selectOne(wrapper); CurrentOrderInfoVo currentOrderInfoVo = new CurrentOrderInfoVo (); if (null != orderInfo) { currentOrderInfoVo.setStatus(orderInfo.getStatus()); currentOrderInfoVo.setOrderId(orderInfo.getId()); currentOrderInfoVo.setIsHasCurrentOrder(true ); } else { currentOrderInfoVo.setIsHasCurrentOrder(false ); } return currentOrderInfoVo; }
远程调用 service-order-client OrderInfoFeignClient
1 2 3 4 5 6 7 @GetMapping("/order/info/searchDriverCurrentOrder/{driverId}") Result<CurrentOrderInfoVo> searchDriverCurrentOrder (@PathVariable("driverId") Long driverId) ;
司机端web接口 web-driver OrderController
1 2 3 4 5 6 7 @Operation(summary = "司机端查找当前订单") @LoginDetection @GetMapping("/searchDriverCurrentOrder") public Result<CurrentOrderInfoVo> searchDriverCurrentOrder () { Long driverId = AuthContextHolder.getUserId(); return Result.ok(orderService.searchDriverCurrentOrder(driverId)); }
service
1 2 3 4 5 @Override public CurrentOrderInfoVo searchDriverCurrentOrder (Long driverId) { return orderInfoFeignClient.searchDriverCurrentOrder(driverId).getData(); }
获取订单信息 订单微服务接口 service-order OrderInfoController
1 2 3 4 5 @Operation(summary = "根据订单id获取订单信息") @GetMapping("/getOrderInfo/{orderId}") public Result<OrderInfo> getOrderInfo (@PathVariable Long orderId) { return Result.ok(orderInfoService.getById(orderId)); }
远程调用 OrderInfoFeignClient
1 2 3 4 5 6 7 @GetMapping("/order/info/getOrderInfo/{orderId}") Result<OrderInfo> getOrderInfo (@PathVariable("orderId") Long orderId) ;
乘客端web接口 OrderController
1 2 3 4 5 6 7 @Operation(summary = "获取订单信息") @LoginDetection @GetMapping("/getOrderInfo/{orderId}") public Result<OrderInfoVo> getOrderInfo (@PathVariable Long orderId) { Long customerId = AuthContextHolder.getUserId(); return Result.ok(orderService.getOrderInfo(orderId, customerId)); }
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Override public OrderInfoVo getOrderInfo (Long orderId, Long customerId) { OrderInfo orderInfo = orderInfoFeignClient.getOrderInfo(orderId).getData(); if (orderInfo.getCustomerId() != customerId) { throw new GuiguException (ResultCodeEnum.ILLEGAL_REQUEST); } OrderInfoVo orderInfoVo = new OrderInfoVo (); orderInfoVo.setOrderId(orderId); BeanUtils.copyProperties(orderInfo,orderInfoVo); return orderInfoVo; }
司机端web接口 OrderController
1 2 3 4 5 6 7 @Operation(summary = "获取订单账单详细信息") @LoginDetection @GetMapping("/getOrderInfo/{orderId}") public Result<OrderInfoVo> getOrderInfo (@PathVariable Long orderId) { Long driverId = AuthContextHolder.getUserId(); return Result.ok(orderService.getOrderInfo(orderId, driverId)); }
service
1 2 3 4 5 6 7 8 9 10 11 @Override public OrderInfoVo getOrderInfo (Long orderId, Long driverId) { OrderInfo orderInfo = orderInfoFeignClient.getOrderInfo(orderId).getData(); if (orderInfo.getDriverId() != driverId) { throw new GuiguException (ResultCodeEnum.ILLEGAL_REQUEST); } OrderInfoVo orderInfoVo = new OrderInfoVo (); orderInfoVo.setOrderId(orderId); BeanUtils.copyProperties(orderInfo,orderInfoVo); return orderInfoVo; }
司乘同显 司机端同显
司机所在地址就是司乘同显开始位置,订单地址就是司乘同显的终点
计算司机司乘同显最佳路线
司机端web OrderController
1 2 3 4 5 6 @Operation(summary = "计算最佳驾驶线路") @LoginDetection @PostMapping("/calculateDrivingLine") public Result<DrivingLineVo> calculateDrivingLine (@RequestBody CalculateDrivingLineForm calculateDrivingLineForm) { return Result.ok(orderService.calculateDrivingLine(calculateDrivingLineForm)); }
service
1 2 3 4 5 6 7 8 9 @Autowired private MapFeignClient mapFeignClient;@Override public DrivingLineVo calculateDrivingLine (CalculateDrivingLineForm calculateDrivingLineForm) { Result<DrivingLineVo> drivingLineVoResult = mapFeignClient.calculateDrivingLine(calculateDrivingLineForm); return drivingLineVoResult.getData(); }
更新位置到redis
地图微服务接口service-map LocationController
1 2 3 4 5 @Operation(summary = "司机赶往代驾起始点:更新订单地址到缓存") @PostMapping("/updateOrderLocationToCache") public Result<Boolean> updateOrderLocationToCache (@RequestBody UpdateOrderLocationForm updateOrderLocationForm) { return Result.ok(locationService.updateOrderLocationToCache(updateOrderLocationForm)); }
service
1 2 3 4 5 6 7 8 9 10 11 12 @Override public Boolean updateOrderLocationToCache (UpdateOrderLocationForm updateOrderLocationForm) { OrderLocationVo orderLocationVo = new OrderLocationVo (); orderLocationVo.setLongitude(updateOrderLocationForm.getLongitude()); orderLocationVo.setLatitude(updateOrderLocationForm.getLatitude()); String key = RedisConstant.UPDATE_ORDER_LOCATION + updateOrderLocationForm.getOrderId(); redisTemplate.opsForValue().set(key, orderLocationVo); return true ; }
远程调用service-map-client LocationFeignClient
1 2 3 4 5 6 7 @PostMapping("/map/location/updateOrderLocationToCache") Result<Boolean> updateOrderLocationToCache (@RequestBody UpdateOrderLocationForm updateOrderLocationForm) ;
司机web端 LocationController
1 2 3 4 5 6 @Operation(summary = "司机赶往代驾起始点:更新订单位置到Redis缓存") @LoginDetection @PostMapping("/updateOrderLocationToCache") public Result updateOrderLocationToCache (@RequestBody UpdateOrderLocationForm updateOrderLocationForm) { return Result.ok(locationService.updateOrderLocationToCache(updateOrderLocationForm)); }
service
1 2 3 4 5 @Override public Boolean updateOrderLocationToCache (UpdateOrderLocationForm updateOrderLocationForm) { return locationFeignClient.updateOrderLocationToCache(updateOrderLocationForm).getData(); }
获取司机基本信息
司机微服务接口service-driver DriverInfoController
1 2 3 4 5 @Operation(summary = "获取司机基本信息") @GetMapping("/getDriverInfo/{driverId}") public Result<DriverInfoVo> getDriverInfoOrder (@PathVariable Long driverId) { return Result.ok(driverInfoService.getDriverInfoOrder(driverId)); }
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Override public DriverInfoVo getDriverInfoOrder (Long driverId) { DriverInfo driverInfo = driverInfoMapper.selectById(driverId); DriverInfoVo driverInfoVo = new DriverInfoVo (); BeanUtils.copyProperties(driverInfo,driverInfoVo); int currentYear = new DateTime ().getYear(); int firstYear = new DateTime (driverInfo.getDriverLicenseIssueDate()).getYear(); int driverLicenseAge = currentYear - firstYear; driverInfoVo.setDriverLicenseAge(driverLicenseAge); return driverInfoVo; }
远程调用 DriverInfoFeignClient
1 2 3 4 5 6 7 @GetMapping("/driver/info/getDriverInfo/{driverId}") Result<DriverInfoVo> getDriverInfo (@PathVariable("driverId") Long driverId) ;
乘客端web接口 OrderController
1 2 3 4 5 6 7 @Operation(summary = "根据订单id获取司机基本信息") @LoginDetection @GetMapping("/getDriverInfo/{orderId}") public Result<DriverInfoVo> getDriverInfo (@PathVariable Long orderId) { Long customerId = AuthContextHolder.getUserId(); return Result.ok(orderService.getDriverInfo(orderId, customerId)); }
service
1 2 3 4 5 6 7 8 9 10 @Override public DriverInfoVo getDriverInfo (Long orderId, Long customerId) { OrderInfo orderInfo = orderInfoFeignClient.getOrderInfo(orderId).getData(); if (orderInfo.getCustomerId() != customerId) { throw new GuiguException (ResultCodeEnum.DATA_ERROR); } return driverInfoFeignClient.getDriverInfo(orderInfo.getDriverId()).getData(); }
乘客端获取司机经纬度位置
地图微服务接口service-map LocationController
1 2 3 4 5 @Operation(summary = "司机赶往代驾起始点:获取订单经纬度位置") @GetMapping("/getCacheOrderLocation/{orderId}") public Result<OrderLocationVo> getCacheOrderLocation (@PathVariable Long orderId) { return Result.ok(locationService.getCacheOrderLocation(orderId)); }
service
1 2 3 4 5 6 7 @Override public OrderLocationVo getCacheOrderLocation (Long orderId) { String key = RedisConstant.UPDATE_ORDER_LOCATION + orderId; OrderLocationVo orderLocationVo = (OrderLocationVo)redisTemplate.opsForValue().get(key); return orderLocationVo; }
远程调用 LocationFeignClient
1 2 3 4 5 6 7 @GetMapping("/map/location/getCacheOrderLocation/{orderId}") Result<OrderLocationVo> getCacheOrderLocation (@PathVariable("orderId") Long orderId) ;
乘客端web接口 OrderController
1 2 3 4 5 @Operation(summary = "司机赶往代驾起始点:获取订单经纬度位置") @GetMapping("/getCacheOrderLocation/{orderId}") public Result<OrderLocationVo> getCacheOrderLocation (@PathVariable Long orderId) { return Result.ok(orderService.getCacheOrderLocation(orderId)); }
service
1 2 3 4 5 @Override public OrderLocationVo getCacheOrderLocation (Long orderId) { return locationFeignClient.getCacheOrderLocation(orderId).getData(); }
乘客端同显 乘客端web接口 OrderController
1 2 3 4 5 6 @Operation(summary = "计算最佳驾驶线路") @LoginDetection @PostMapping("/calculateDrivingLine") public Result<DrivingLineVo> calculateDrivingLine (@RequestBody CalculateDrivingLineForm calculateDrivingLineForm) { return Result.ok(orderService.calculateDrivingLine(calculateDrivingLineForm)); }
service
1 2 3 4 5 @Override public DrivingLineVo calculateDrivingLine (CalculateDrivingLineForm calculateDrivingLineForm) { return mapFeignClient.calculateDrivingLine(calculateDrivingLineForm).getData(); }
司机到达起始点
司机到达起始点后更新订单数据
更新订单状态:司机已到达
更新订单到达时间
订单微服务接口service-order OrderInfoController
1 2 3 4 5 @Operation(summary = "司机到达起始点") @GetMapping("/driverArriveStartLocation/{orderId}/{driverId}") public Result<Boolean> driverArriveStartLocation (@PathVariable Long orderId, @PathVariable Long driverId) { return Result.ok(orderInfoService.driverArriveStartLocation(orderId, driverId)); }
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Override public Boolean driverArriveStartLocation (Long orderId, Long driverId) { LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper <>(); wrapper.eq(OrderInfo::getId,orderId); wrapper.eq(OrderInfo::getDriverId,driverId); OrderInfo orderInfo = new OrderInfo (); orderInfo.setStatus(OrderStatus.DRIVER_ARRIVED.getStatus()); orderInfo.setArriveTime(new Date ()); int rows = orderInfoMapper.update(orderInfo, wrapper); if (rows == 1 ) { return true ; } else { throw new GuiguException (ResultCodeEnum.UPDATE_ERROR); } }
远程调用service-order-client OrderInfoFeignClient
1 2 3 4 5 6 7 8 @GetMapping("/order/info/driverArriveStartLocation/{orderId}/{driverId}") Result<Boolean> driverArriveStartLocation (@PathVariable("orderId") Long orderId, @PathVariable("driverId") Long driverId) ;
司机web调用 OrderController
1 2 3 4 5 6 7 @Operation(summary = "司机到达代驾起始地点") @LoginDetection @GetMapping("/driverArriveStartLocation/{orderId}") public Result<Boolean> driverArriveStartLocation (@PathVariable Long orderId) { Long driverId = AuthContextHolder.getUserId(); return Result.ok(orderService.driverArriveStartLocation(orderId, driverId)); }
1 2 3 4 5 @Override public Boolean driverArriveStartLocation (Long orderId, Long driverId) { return orderInfoFeignClient.driverArriveStartLocation(orderId, driverId).getData(); }
司机更新代驾车辆信息 订单微服务接口service-order OrderInfoController
1 2 3 4 5 @Operation(summary = "更新代驾车辆信息") @PostMapping("/updateOrderCart") public Result<Boolean> updateOrderCart (@RequestBody UpdateOrderCartForm updateOrderCartForm) { return Result.ok(orderInfoService.updateOrderCart(updateOrderCartForm)); }
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Override public Boolean updateOrderCart (UpdateOrderCartForm updateOrderCartForm) { LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper <>(); wrapper.eq(OrderInfo::getId,updateOrderCartForm.getOrderId()); wrapper.eq(OrderInfo::getDriverId,updateOrderCartForm.getDriverId()); OrderInfo orderInfo = new OrderInfo (); BeanUtils.copyProperties(updateOrderCartForm,orderInfo); orderInfo.setStatus(OrderStatus.UPDATE_CART_INFO.getStatus()); int rows = orderInfoMapper.update(orderInfo, wrapper); if (rows == 1 ) { return true ; } else { throw new GuiguException (ResultCodeEnum.UPDATE_ERROR); } }
远程调用 OrderInfoFeignClient
1 2 3 4 5 6 7 @PostMapping("/order/info//updateOrderCart") Result<Boolean> updateOrderCart (@RequestBody UpdateOrderCartForm updateOrderCartForm) ;
司机web端接口 OrderController
1 2 3 4 5 6 7 8 @Operation(summary = "更新代驾车辆信息") @LoginDetection @PostMapping("/updateOrderCart") public Result<Boolean> updateOrderCart (@RequestBody UpdateOrderCartForm updateOrderCartForm) { Long driverId = AuthContextHolder.getUserId(); updateOrderCartForm.setDriverId(driverId); return Result.ok(orderService.updateOrderCart(updateOrderCartForm)); }
service
1 2 3 4 5 @Override public Boolean updateOrderCart (UpdateOrderCartForm updateOrderCartForm) { return orderInfoFeignClient.updateOrderCart(updateOrderCartForm).getData(); }
测试
启动项目:
XxlJobAdminApplication
ServerGatewayApplication :8600/
ServiceCustomerApplication :8501/
ServiceDispatchApplication :8509/
ServiceDriverApplication :8502/
ServiceMapApplication :8503/
ServiceOrderApplication:8505/
ServiceRulesApplication :8504/
WebCustomerApplication :8601/
WebDriverApplication :8602/
清除之前的数据
启动微信开发者工具
修改前面的代码web-driver
FileController
1 2 3 4 5 6 7 8 9 10 11 12 13 @Autowired private CosService cosService; @Operation(summary = "上传") @PostMapping("/upload") public Result<String> upload (@RequestPart("file") MultipartFile file, @RequestParam(name = "path",defaultValue = "auth") String path) { CosUploadVo cosUploadVo = cosService.uploadFile(file,path); String showUrl = cosUploadVo.getShowUrl(); return Result.ok(showUrl); }
订单执行(二) 开始服务
订单微服务接口 OrderInfoController
1 2 3 4 5 6 @Operation(summary = "开始服务") @PostMapping("/startDrive") public Result<Boolean> startDriver (@RequestBody StartDriveForm startDriveForm) { Boolean flag = orderInfoService.startDriver(startDriveForm); return Result.ok(flag); }
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Override public Boolean startDriver (StartDriveForm startDriveForm) { LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper <>(); wrapper.eq(OrderInfo::getId,startDriveForm.getOrderId()); wrapper.eq(OrderInfo::getDriverId,startDriveForm.getDriverId()); OrderInfo orderInfo = new OrderInfo (); orderInfo.setStatus(OrderStatus.START_SERVICE.getStatus()); orderInfo.setStartServiceTime(new Date ()); int rows = orderInfoMapper.update(orderInfo, wrapper); if (rows == 1 ) { return true ; } else { throw new GuiguException (ResultCodeEnum.UPDATE_ERROR); } }
远程调用 OrderInfoFeignClient
1 2 3 4 5 6 7 @PostMapping("/order/info/startDrive") Result<Boolean> startDrive (@RequestBody StartDriveForm startDriveForm) ;
司机web端调用 OrderController
1 2 3 4 5 6 7 8 @Operation(summary = "开始代驾服务") @LoginDetection @PostMapping("/startDrive") public Result<Boolean> startDrive (@RequestBody StartDriveForm startDriveForm) { Long driverId = AuthContextHolder.getUserId(); startDriveForm.setDriverId(driverId); return Result.ok(orderService.startDrive(startDriveForm)); }
service
1 2 3 4 5 @Override public Boolean startDrive (StartDriveForm startDriveForm) { return orderInfoFeignClient.startDrive(startDriveForm).getData(); }
批量保存订单位置信息
开始服务后,司机端会实时收集司机位置
定时批量上传位置到后台服务保存到MongoDB
地图微服务接口 LocationController
1 2 3 4 5 @Operation(summary = "批量保存代驾服务订单位置") @PostMapping("/saveOrderServiceLocation") public Result<Boolean> saveOrderServiceLocation (@RequestBody List<OrderServiceLocationForm> orderLocationServiceFormList) { return Result.ok(locationService.saveOrderServiceLocation(orderLocationServiceFormList)); }
编写接口
1 2 3 @Repository public interface OrderServiceLocationRepository extends MongoRepository <OrderServiceLocation, String> { }
导入依赖
1 2 3 4 5 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-data-mongodb</artifactId > </dependency >
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Autowired private OrderServiceLocationRepository orderServiceLocationRepository;@Override public Boolean saveOrderServiceLocation (List<OrderServiceLocationForm> orderLocationServiceFormList) { List<OrderServiceLocation> list = new ArrayList <>(); orderLocationServiceFormList.forEach(orderServiceLocationForm->{ OrderServiceLocation orderServiceLocation = new OrderServiceLocation (); BeanUtils.copyProperties(orderServiceLocationForm,orderServiceLocation); orderServiceLocation.setId(ObjectId.get().toString()); orderServiceLocation.setCreateTime(new Date ()); list.add(orderServiceLocation); }); orderServiceLocationRepository.saveAll(list); return true ; }
远程调用 LocationFeignClient
1 2 3 4 5 6 7 @PostMapping("/map/location/saveOrderServiceLocation") Result<Boolean> saveOrderServiceLocation (@RequestBody List<OrderServiceLocationForm> orderLocationServiceFormList) ;
司机web端调用 LocationController
1 2 3 4 5 @Operation(summary = "开始代驾服务:保存代驾服务订单位置") @PostMapping("/saveOrderServiceLocation") public Result<Boolean> saveOrderServiceLocation (@RequestBody List<OrderServiceLocationForm> orderLocationServiceFormList) { return Result.ok(locationService.saveOrderServiceLocation(orderLocationServiceFormList)); }
service
1 2 3 4 5 @Override public Boolean saveOrderServiceLocation (List<OrderServiceLocationForm> orderLocationServiceFormList) { return locationFeignClient.saveOrderServiceLocation(orderLocationServiceFormList).getData(); }
获取订单最后一个位置
地图微服务接口 LocationController
1 2 3 4 5 @Operation(summary = "代驾服务:获取订单服务最后一个位置信息") @GetMapping("/getOrderServiceLastLocation/{orderId}") public Result<OrderServiceLastLocationVo> getOrderServiceLastLocation (@PathVariable Long orderId) { return Result.ok(locationService.getOrderServiceLastLocation(orderId)); }
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Autowired private MongoTemplate mongoTemplate;@Override public OrderServiceLastLocationVo getOrderServiceLastLocation (Long orderId) { Query query = new Query (); query.addCriteria(Criteria.where("orderId" ).is(orderId)); query.with(Sort.by(Sort.Order.desc("createTime" ))); query.limit(1 ); OrderServiceLocation orderServiceLocation = mongoTemplate.findOne(query, OrderServiceLocation.class); OrderServiceLastLocationVo orderServiceLastLocationVo = new OrderServiceLastLocationVo (); BeanUtils.copyProperties(orderServiceLocation,orderServiceLastLocationVo); return orderServiceLastLocationVo; }
远程调用 LocationFeignClient
1 2 3 4 5 6 7 @GetMapping("/map/location/getOrderServiceLastLocation/{orderId}") Result<OrderServiceLastLocationVo> getOrderServiceLastLocation (@PathVariable Long orderId) ;
乘客web端调用 OrderController
1 2 3 4 5 6 @Operation(summary = "代驾服务:获取订单服务最后一个位置信息") @LoginDetection @GetMapping("/getOrderServiceLastLocation/{orderId}") public Result<OrderServiceLastLocationVo> getOrderServiceLastLocation (@PathVariable Long orderId) { return Result.ok(orderService.getOrderServiceLastLocation(orderId)); }
service
1 2 3 4 5 @Override public OrderServiceLastLocationVo getOrderServiceLastLocation (Long orderId) { return locationFeignClient.getOrderServiceLastLocation(orderId).getData(); }
Minio上传接口
司机服务过程中,司机端小程序实时采集录音,把录音和对话文本上传到后台服务,把完整监控保存到Minio
Minio安装 使用docker安装
1 2 3 4 5 6 7 8 9 10 11 12 13 // 创建数据存储目录 mkdir -p ~/minio/data // 创建minio docker run \ -p 9000:9000 \ -p 9090:9090 \ --name minio \ -v ~/minio/data:/data \ -e "MINIO_ROOT_USER=admin" \ -e "MINIO_ROOT_PASSWORD=admin123456" \ -d \ quay.io/minio/minio server /data --console-address ":9090"
2 安装到windows里面
找到minio安装文件,放到没有中文没有空格目录
创建空文件夹,作为数据存储目录
在windows使用命令启动Minio服务
– minio.exe server D:\Desktop\hhsqdmz\sort\minio\data
Minio启动
司机端web接口 导入依赖
1 2 3 4 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-data-mongodb</artifactId > </dependency >
修改配置文件common-account.yaml
1 2 3 4 5 minio: endpointUrl: http://localhost:9000 accessKey: minioadmin secreKey: minioadmin bucketName: daijia
创建配置类,读取minio的值
1 2 3 4 5 6 7 8 9 10 @Configuration @ConfigurationProperties(prefix="minio") @Data public class MinioProperties { private String endpointUrl; private String accessKey; private String secreKey; private String bucketName; }
FileController
1 2 3 4 5 6 7 8 9 @Autowired private FileService fileService;@Operation(summary = "上传") @PostMapping("/upload") public Result<String> upload (@RequestPart("file") MultipartFile file) { String url = fileService.upload(file); return Result.ok(url); }
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 @Autowired private MinioProperties minioProperties; @Override public String upload (MultipartFile file) { try { MinioClient minioClient = MinioClient.builder() .endpoint(minioProperties.getEndpointUrl()) .credentials(minioProperties.getAccessKey(), minioProperties.getSecreKey()) .build(); boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(minioProperties.getBucketName()).build()); if (!found) { minioClient.makeBucket(MakeBucketArgs.builder().bucket(minioProperties.getBucketName()).build()); } else { System.out.println("Bucket 'daijia' already exists." ); } String extFileName = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("." )); String fileName = new SimpleDateFormat ("yyyyMMdd" ) .format(new Date ()) + "/" + UUID.randomUUID().toString().replace("-" , "" ) + "." + extFileName; PutObjectArgs putObjectArgs = PutObjectArgs.builder() .bucket(minioProperties.getBucketName()) .stream(file.getInputStream(), file.getSize(), -1 ) .object(fileName) .build(); minioClient.putObject(putObjectArgs) ; return minioProperties.getEndpointUrl() + "/" + minioProperties.getBucketName() + "/" + fileName ; } catch (Exception e) { throw new GuiguException (ResultCodeEnum.DATA_ERROR); } }
保存订单监控记录数据
订单执行过程中,记录对话信息
在前端小程序,同声传译,把录音转换文本,保存文本内存
订单微服务接口. 导入依赖
1 2 3 4 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-data-mongodb</artifactId > </dependency >
添加MongoRepository
1 2 3 @Repository public interface OrderMonitorRecordRepository extends MongoRepository <OrderMonitorRecord, String> { }
OrderMonitorController
1 2 3 4 5 6 7 8 @Autowired private OrderMonitorService orderMonitorService; @Operation(summary = "保存订单监控记录数据") @PostMapping("/saveOrderMonitorRecord") public Result<Boolean> saveMonitorRecord (@RequestBody OrderMonitorRecord orderMonitorRecord) { return Result.ok(orderMonitorService.saveOrderMonitorRecord(orderMonitorRecord)); }
service
1 2 3 4 5 6 7 8 9 @Autowired private OrderMonitorRecordRepository orderMonitorRecordRepository; @Override public Boolean saveOrderMonitorRecord (OrderMonitorRecord orderMonitorRecord) { orderMonitorRecordRepository.save(orderMonitorRecord); return true ; }
远程调用 OrderMonitorFeignClient
1 2 3 4 5 6 7 @PostMapping("/order/monitor/saveOrderMonitorRecord") Result<Boolean> saveMonitorRecord (@RequestBody OrderMonitorRecord orderMonitorRecord) ;
司机端web调用 MonitorController
1 2 3 4 5 6 7 8 9 10 @Autowired private MonitorService monitorService; @Operation(summary = "上传录音") @PostMapping("/upload") public Result<Boolean> upload (@RequestParam("file") MultipartFile file, OrderMonitorForm orderMonitorForm) { return Result.ok(monitorService.upload(file, orderMonitorForm)); }
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Autowired private FileService fileService; @Autowired private OrderMonitorFeignClient orderMonitorFeignClient; @Override public Boolean upload (MultipartFile file, OrderMonitorForm orderMonitorForm) { String url = fileService.upload(file); OrderMonitorRecord orderMonitorRecord = new OrderMonitorRecord (); orderMonitorRecord.setOrderId(orderMonitorForm.getOrderId()); orderMonitorRecord.setFileUrl(url); orderMonitorRecord.setContent(orderMonitorForm.getContent()); orderMonitorFeignClient.saveMonitorRecord(orderMonitorRecord); return true ; }
订单监控审核 使用腾讯云数据万象实现自动审核
腾讯云COS图片审核 封装图片审核方法 在service-driver的CiService
1 Boolean imageAuditing (String path) ;
CiServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 @Service public class CiServiceImpl implements CiService { @Autowired private TencentCloudProperties tencentCloudProperties; @Override public Boolean imageAuditing (String path) { ImageAuditingRequest request = new ImageAuditingRequest (); request.setObjectKey(path); COSClient client = this .getCosClient(); ImageAuditingResponse response = client.imageAuditing(request); client.shutdown(); if (!response.getPornInfo().getHitFlag().equals("0" ) || !response.getAdsInfo().getHitFlag().equals("0" ) || !response.getTerroristInfo().getHitFlag().equals("0" ) || !response.getPoliticsInfo().getHitFlag().equals("0" ) ) { return false ; } return true ; } public COSClient getCosClient () { String secretId = tencentCloudProperties.getSecretId(); String secretKey = tencentCloudProperties.getSecretKey(); COSCredentials cred = new BasicCOSCredentials (secretId, secretKey); Region region = new Region (tencentCloudProperties.getRegion()); ClientConfig clientConfig = new ClientConfig (region); clientConfig.setHttpProtocol(HttpProtocol.https); COSClient cosClient = new COSClient (cred, clientConfig); return cosClient; } }
腾讯云COS图片添加审核 CosServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 @Autowired private CiService ciService; @Override public CosUploadVo upload (MultipartFile file, String path) { COSClient cosClient = this .getCosClient(); ObjectMetadata meta = new ObjectMetadata (); meta.setContentLength(file.getSize()); meta.setContentEncoding("UTF-8" ); meta.setContentType(file.getContentType()); String fileType = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("." )); String uploadPath = "/driver/" + path + "/" + UUID.randomUUID().toString().replaceAll("-" , "" ) + fileType; try { putObjectRequest = new PutObjectRequest (tencentCloudProperties.getBucketPrivate(), uploadPath, file.getInputStream(), meta); } catch (IOException e) { throw new RuntimeException (e); } putObjectRequest.setStorageClass(StorageClass.Standard); PutObjectResult putObjectResult = cosClient.putObject(putObjectRequest); cosClient.shutdown(); Boolean imageAuditing = ciService.imageAuditing(uploadPath); if (!imageAuditing){ cosClient.deleteObject(tencentCloudProperties.getBucketPrivate(), uploadPath); throw new GuiguException (ResultCodeEnum.IMAGE_AUDITION_FAIL); } CosUploadVo cosUploadVo = new CosUploadVo (); cosUploadVo.setUrl(uploadPath); String imageUrl = this .getImageUrl(uploadPath); cosUploadVo.setShowUrl(imageUrl); return cosUploadVo; }
封装文本审核接口 司机微服务接口 CiController
1 2 3 4 5 6 7 8 @Autowired private CiService ciService; @Operation(summary = "文本审核") @PostMapping("/textAuditing") public Result<TextAuditingVo> textAuditing (@RequestBody String content) { return Result.ok(ciService.textAuditing(content)); }
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 @Override public TextAuditingVo textAuditing (String content) { if (!StringUtils.hasText(content)) { TextAuditingVo textAuditingVo = new TextAuditingVo (); textAuditingVo.setResult("0" ); return textAuditingVo; } COSClient cosClient = this .getCosClient(); TextAuditingRequest request = new TextAuditingRequest (); request.setBucketName(tencentCloudProperties.getBucketPrivate()); byte [] encoder = org.apache.commons.codec.binary.Base64.encodeBase64(content.getBytes()); String contentBase64 = new String (encoder); request.getInput().setContent(contentBase64); request.getConf().setDetectType("all" ); TextAuditingResponse response = cosClient.createAuditingTextJobs(request); AuditingJobsDetail detail = response.getJobsDetail(); TextAuditingVo textAuditingVo = new TextAuditingVo (); if ("Success" .equals(detail.getState())) { String result = detail.getResult(); StringBuffer keywords = new StringBuffer (); List<SectionInfo> sectionInfoList = detail.getSectionList(); for (SectionInfo info : sectionInfoList) { String pornInfoKeyword = info.getPornInfo().getKeywords(); String illegalInfoKeyword = info.getIllegalInfo().getKeywords(); String abuseInfoKeyword = info.getAbuseInfo().getKeywords(); if (pornInfoKeyword.length() > 0 ) { keywords.append(pornInfoKeyword).append("," ); } if (illegalInfoKeyword.length() > 0 ) { keywords.append(illegalInfoKeyword).append("," ); } if (abuseInfoKeyword.length() > 0 ) { keywords.append(abuseInfoKeyword).append("," ); } } textAuditingVo.setResult(result); textAuditingVo.setKeywords(keywords.toString()); } return textAuditingVo; }
远程调用 CiFeignClient
1 2 3 4 5 6 7 @PostMapping("/ci/textAuditing") Result<TextAuditingVo> textAuditing (@RequestBody String content) ;
订单监控接口完善 修改web-driver的MonitorServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 @Autowired private FileService fileService; @Autowired private OrderMonitorFeignClient orderMonitorFeignClient; @Autowired private CiFeignClient ciFeignClient; @Override public Boolean upload (MultipartFile file, OrderMonitorForm orderMonitorForm) { String url = fileService.upload(file); OrderMonitorRecord orderMonitorRecord = new OrderMonitorRecord (); orderMonitorRecord.setOrderId(orderMonitorForm.getOrderId()); orderMonitorRecord.setFileUrl(url); orderMonitorRecord.setContent(orderMonitorForm.getContent()); TextAuditingVo textAuditingVo = ciFeignClient.textAuditing(orderMonitorForm.getContent()).getData(); orderMonitorRecord.setResult(textAuditingVo.getResult()); orderMonitorRecord.setKeywords(textAuditingVo.getKeywords()); orderMonitorFeignClient.saveMonitorRecord(orderMonitorRecord); return true ; }
订单执行(三) 计算订单实际里程
在MongoDB保存订单过程中司机位置信息,把MongoDB存储司机位置信息获取出来,以时间排序,连接每个点,成为实际距离
准备计算距离工具类 common-util中的LocationUtil
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public static double getDistance (double lat1, double lng1, double lat2, double lng2) { double radLat1 = rad(lat1); double radLat2 = rad(lat2); double a = radLat1 - radLat2; double b = rad(lng1) - rad(lng2); double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2 ), 2 ) + Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2 ), 2 ))); s = s * EARTH_RADIUS; s = Math.round(s * 10000d ) / 10000d ; s = s * 1000 ; return s; }
地图微服务接口 LocationController
1 2 3 4 5 @Operation(summary = "代驾服务:计算订单实际里程") @GetMapping("/calculateOrderRealDistance/{orderId}") public Result<BigDecimal> calculateOrderRealDistance (@PathVariable Long orderId) { return Result.ok(locationService.calculateOrderRealDistance(orderId)); }
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 @Override public BigDecimal calculateOrderRealDistance (Long orderId) { List<OrderServiceLocation> list = orderServiceLocationRepository.findByOrderIdOrderByCreateTimeAsc(orderId); double realDistance = 0 ; if (!CollectionUtils.isEmpty(list)) { for (int i = 0 ,size = list.size()-1 ; i < size; i++) { OrderServiceLocation location1 = list.get(i); OrderServiceLocation location2 = list.get(i + 1 ); double distance = LocationUtil.getDistance(location1.getLatitude().doubleValue(), location1.getLongitude().doubleValue(), location2.getLatitude().doubleValue(), location2.getLongitude().doubleValue()); realDistance += distance; } } return new BigDecimal (realDistance); }
远程调用 LocationFeignClient
1 2 3 4 5 6 7 @GetMapping("/map/location/calculateOrderRealDistance/{orderId}") Result<BigDecimal> calculateOrderRealDistance (@PathVariable Long orderId) ;
计算系统奖励 创建规则文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package com.atguigu.daijia import com.atguigu.daijia.model.form.rules.RewardRuleRequest; import java.math.BigDecimal; import java.math.RoundingMode; global com.atguigu.daijia.model.vo.rules.RewardRuleResponse rewardRuleResponse; rule "00:00:00-06:59:59 完成5单后 每单奖励5元" salience 10 no-loop true when $rule:RewardRuleRequest(startTime >= "00:00:00" && startTime <= "06:59:59" && orderNum > 5 ) then rewardRuleResponse.setRewardAmount(new BigDecimal ("5.0" )); System.out.println("00:00:00-06:59:59 奖励:" + rewardRuleResponse.getRewardAmount() + "元" ); end rule "07:00:00-23:59:59 完成10单后 每单奖励2元" salience 10 no-loop true when $rule:RewardRuleRequest(startTime >= "07:00:00" && startTime <= "23:59:59" && orderNum > 10 ) then rewardRuleResponse.setRewardAmount(new BigDecimal ("2.0" )); System.out.println("00:00:00-06:59:59 奖励:" + rewardRuleResponse.getRewardAmount() + "元" ); end
添加规则引擎工具类 在service-rules中utils包下创建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class DroolsHelper { private static final String RULES_CUSTOMER_RULES_DRL = "rules/FeeRule.drl" ; public static KieSession loadForRule (String drlStr) { KieServices kieServices = KieServices.Factory.get(); KieFileSystem kieFileSystem = kieServices.newKieFileSystem(); kieFileSystem.write( ResourceFactory.newClassPathResource(drlStr)); KieBuilder kb = kieServices.newKieBuilder(kieFileSystem); kb.buildAll(); KieModule kieModule = kb.getKieModule(); KieContainer kieContainer = kieServices.newKieContainer(kieModule.getReleaseId()); return kieContainer.newKieSession(); } }
规则微服务接口 RewardRuleController
1 2 3 4 5 6 7 8 9 @Autowired private RewardRuleService rewardRuleService; @Operation(summary = "计算订单奖励费用") @PostMapping("/calculateOrderRewardFee") public Result<RewardRuleResponseVo> calculateOrderRewardFee (@RequestBody RewardRuleRequestForm rewardRuleRequestForm) { return Result.ok(rewardRuleService.calculateOrderRewardFee(rewardRuleRequestForm)); }
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 @Override public RewardRuleResponseVo calculateOrderRewardFee (RewardRuleRequestForm rewardRuleRequestForm) { RewardRuleRequest rewardRuleRequest = new RewardRuleRequest (); rewardRuleRequest.setOrderNum(rewardRuleRequestForm.getOrderNum()); KieSession kieSession = DroolsHelper.loadForRule(RULES_CUSTOMER_RULES_DRL); RewardRuleResponse rewardRuleResponse = new RewardRuleResponse (); kieSession.setGlobal("rewardRuleResponse" ,rewardRuleResponse); kieSession.insert(rewardRuleRequest); kieSession.fireAllRules(); kieSession.dispose(); RewardRuleResponseVo rewardRuleResponseVo = new RewardRuleResponseVo (); rewardRuleResponseVo.setRewardAmount(rewardRuleResponse.getRewardAmount()); return rewardRuleResponseVo; }
远程调用 RewardRuleFeignClient
1 2 3 4 5 6 7 @PostMapping("/rules/reward/calculateOrderRewardFee") Result<RewardRuleResponseVo> calculateOrderRewardFee (@RequestBody RewardRuleRequestForm rewardRuleRequestForm) ;
根据时间段获取订单数量 订单微服务接口 OrderInfoController
1 2 3 4 5 @Operation(summary = "根据时间段获取订单数") @GetMapping("/getOrderNumByTime/{startTime}/{endTime}") public Result<Long> getOrderNumByTime (@PathVariable String startTime, @PathVariable String endTime) { return Result.ok(orderInfoService.getOrderNumByTime(startTime, endTime)); }
service
1 2 3 4 5 6 7 8 9 @Override public Long getOrderNumByTime (String startTime, String endTime) { LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper <>(); wrapper.ge(OrderInfo::getStartServiceTime,startTime); wrapper.lt(OrderInfo::getStartServiceTime,endTime); Long count = orderInfoMapper.selectCount(wrapper); return count; }
远程调用 OrderInfoFeignClient
1 2 3 4 5 6 7 8 @GetMapping("/order/info/getOrderNumByTime/{startTime}/{endTime}") Result<Long> getOrderNumByTime (@PathVariable("startTime") String startTime, @PathVariable("endTime") String endTime) ;
计算分账信息 创建规则文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 //package对应的不一定是真正的目录,可以任意写com.abc,同一个包下的drl文件可以相互访问 package com.atguigu.daijia import com.atguigu.daijia.model.form.rules.ProfitsharingRuleRequest; import java.math.BigDecimal; import java.math.RoundingMode; global com.atguigu.daijia.model.vo.rules.ProfitsharingRuleResponse profitsharingRuleResponse; //支付微信平台费率:0.6% //global BigDecimal paymentRate = new BigDecimal(0.006); /** 支付微信平台费用 平台费率:0.6% */ rule "支付微信平台费用 平台费率:0.6%" salience 10 //指定优先级,数值越大优先级越高,不指定的情况下由上到下执行 no-loop true //防止陷入死循环 when /*规则条件,到工作内存中查找FeeRuleRequest对象 里面出来的结果只能是ture或者false $rule是绑定变量名,可以任意命名,官方推荐$符号,定义了绑定变量名,可以在then部分操作fact对象*/ $rule:ProfitsharingRuleRequest() then profitsharingRuleResponse.setOrderAmount($rule.getOrderAmount()); profitsharingRuleResponse.setPaymentRate(new BigDecimal("0.006")); BigDecimal paymentFee = profitsharingRuleResponse.getOrderAmount().multiply(profitsharingRuleResponse.getPaymentRate()).setScale(2, RoundingMode.HALF_UP); profitsharingRuleResponse.setPaymentFee(paymentFee); System.out.println("支付微信平台费用:" + profitsharingRuleResponse.getPaymentFee() + "元"); end /** 订单金额小于等于100 当天完成订单小于等于10单 平台抽成 20% 当天完成订单大于10单 平台抽成 18% */ rule "订单金额小于等于100 当天完成订单小于等于10单" salience 10 //指定优先级,数值越大优先级越高,不指定的情况下由上到下执行 no-loop true //防止陷入死循环 when /*规则条件,到工作内存中查找FeeRuleRequest对象 里面出来的结果只能是ture或者false $rule是绑定变量名,可以任意命名,官方推荐$符号,定义了绑定变量名,可以在then部分操作fact对象*/ $rule:ProfitsharingRuleRequest(orderAmount.doubleValue() <= 100.0 && orderNum <= 10) then BigDecimal totalAmount = profitsharingRuleResponse.getOrderAmount().subtract(profitsharingRuleResponse.getPaymentFee()); BigDecimal platformIncome = totalAmount.multiply(new BigDecimal("0.2")).setScale(2, RoundingMode.HALF_UP); BigDecimal driverTotalIncome = totalAmount.subtract(platformIncome); //代驾司机个税,税率:10% BigDecimal driverTaxFee = driverTotalIncome.multiply(new BigDecimal("0.1")).setScale(2, RoundingMode.HALF_UP); BigDecimal driverIncome = driverTotalIncome.subtract(driverTaxFee); profitsharingRuleResponse.setPlatformIncome(platformIncome); profitsharingRuleResponse.setDriverIncome(driverIncome); profitsharingRuleResponse.setDriverTaxRate(new BigDecimal("0.1")); profitsharingRuleResponse.setDriverTaxFee(driverTaxFee); System.out.println("平台分账收入:" + platformIncome + "元" + ",司机分账收入:" + driverIncome + "元" + ",司机个税:" + driverTaxFee + "元"); end rule "订单金额小于等于100 天完成订单大于10单" salience 10 //指定优先级,数值越大优先级越高,不指定的情况下由上到下执行 no-loop true //防止陷入死循环 when /*规则条件,到工作内存中查找FeeRuleRequest对象 里面出来的结果只能是ture或者false $rule是绑定变量名,可以任意命名,官方推荐$符号,定义了绑定变量名,可以在then部分操作fact对象*/ $rule:ProfitsharingRuleRequest(orderAmount.doubleValue() <= 100.0 && orderNum > 10) then BigDecimal totalAmount = profitsharingRuleResponse.getOrderAmount().subtract(profitsharingRuleResponse.getPaymentFee()); BigDecimal platformIncome = totalAmount.multiply(new BigDecimal("0.18")).setScale(2, RoundingMode.HALF_UP); BigDecimal driverTotalIncome = totalAmount.subtract(platformIncome); //代驾司机个税,税率:10% BigDecimal driverTaxFee = driverTotalIncome.multiply(new BigDecimal("0.1")).setScale(2, RoundingMode.HALF_UP); BigDecimal driverIncome = driverTotalIncome.subtract(driverTaxFee); profitsharingRuleResponse.setPlatformIncome(platformIncome); profitsharingRuleResponse.setDriverIncome(driverIncome); profitsharingRuleResponse.setDriverTaxRate(new BigDecimal("0.1")); profitsharingRuleResponse.setDriverTaxFee(driverTaxFee); System.out.println("平台分账收入:" + platformIncome + "元" + ",司机分账收入:" + driverIncome + "元" + ",司机个税:" + driverTaxFee + "元"); end /** 订单金额大于100 当天完成订单小于等于10单 平台抽成 18% 当天完成订单大于10单 平台抽成 16% */ rule "订单金额大于100 当天完成订单小于等于10单" salience 10 //指定优先级,数值越大优先级越高,不指定的情况下由上到下执行 no-loop true //防止陷入死循环 when /*规则条件,到工作内存中查找FeeRuleRequest对象 里面出来的结果只能是ture或者false $rule是绑定变量名,可以任意命名,官方推荐$符号,定义了绑定变量名,可以在then部分操作fact对象*/ $rule:ProfitsharingRuleRequest(orderAmount.doubleValue() > 100.0 && orderNum <= 10) then BigDecimal totalAmount = profitsharingRuleResponse.getOrderAmount().subtract(profitsharingRuleResponse.getPaymentFee()); BigDecimal platformIncome = totalAmount.multiply(new BigDecimal("0.18")).setScale(2, RoundingMode.HALF_UP); BigDecimal driverTotalIncome = totalAmount.subtract(platformIncome); //代驾司机个税,税率:10% BigDecimal driverTaxFee = driverTotalIncome.multiply(new BigDecimal("0.1")).setScale(2, RoundingMode.HALF_UP); BigDecimal driverIncome = driverTotalIncome.subtract(driverTaxFee); profitsharingRuleResponse.setPlatformIncome(platformIncome); profitsharingRuleResponse.setDriverIncome(driverIncome); profitsharingRuleResponse.setDriverTaxRate(new BigDecimal("0.1")); profitsharingRuleResponse.setDriverTaxFee(driverTaxFee); System.out.println("平台分账收入:" + platformIncome + "元" + ",司机分账收入:" + driverIncome + "元" + ",司机个税:" + driverTaxFee + "元"); end rule "订单金额大于100 天完成订单大于10单" salience 10 //指定优先级,数值越大优先级越高,不指定的情况下由上到下执行 no-loop true //防止陷入死循环 when /*规则条件,到工作内存中查找FeeRuleRequest对象 里面出来的结果只能是ture或者false $rule是绑定变量名,可以任意命名,官方推荐$符号,定义了绑定变量名,可以在then部分操作fact对象*/ $rule:ProfitsharingRuleRequest(orderAmount.doubleValue() > 100.0 && orderNum > 10) then BigDecimal totalAmount = profitsharingRuleResponse.getOrderAmount().subtract(profitsharingRuleResponse.getPaymentFee()); BigDecimal platformIncome = totalAmount.multiply(new BigDecimal("0.18")).setScale(2, RoundingMode.HALF_UP); BigDecimal driverTotalIncome = totalAmount.subtract(platformIncome); //代驾司机个税,税率:10% BigDecimal driverTaxFee = driverTotalIncome.multiply(new BigDecimal("0.1")).setScale(2, RoundingMode.HALF_UP); BigDecimal driverIncome = driverTotalIncome.subtract(driverTaxFee); profitsharingRuleResponse.setPlatformIncome(platformIncome); profitsharingRuleResponse.setDriverIncome(driverIncome); profitsharingRuleResponse.setDriverTaxRate(new BigDecimal("0.1")); profitsharingRuleResponse.setDriverTaxFee(driverTaxFee); System.out.println("平台分账收入:" + platformIncome + "元" + ",司机分账收入:" + driverIncome + "元" + ",司机个税:" + driverTaxFee + "元"); end
规则微服务接口 ProfitsharingRuleController
1 2 3 4 5 6 7 8 @Autowired private ProfitsharingRuleService profitsharingRuleService; @Operation(summary = "计算系统分账费用") @PostMapping("/calculateOrderProfitsharingFee") public Result<ProfitsharingRuleResponseVo> calculateOrderProfitsharingFee (@RequestBody ProfitsharingRuleRequestForm profitsharingRuleRequestForm) { return Result.ok(profitsharingRuleService.calculateOrderProfitsharingFee(profitsharingRuleRequestForm)); }
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 @Autowired private ProfitsharingRuleMapper rewardRuleMapper; private static final String RULES_CUSTOMER_RULES_DRL = "rules/ProfitsharingRule.drl" ; @Override public ProfitsharingRuleResponseVo calculateOrderProfitsharingFee (ProfitsharingRuleRequestForm profitsharingRuleRequestForm) { ProfitsharingRuleRequest profitsharingRuleRequest = new ProfitsharingRuleRequest (); profitsharingRuleRequest.setOrderAmount(profitsharingRuleRequestForm.getOrderAmount()); profitsharingRuleRequest.setOrderNum(profitsharingRuleRequestForm.getOrderNum()); KieSession kieSession = DroolsHelper.loadForRule(RULES_CUSTOMER_RULES_DRL); ProfitsharingRuleResponse profitsharingRuleResponse = new ProfitsharingRuleResponse (); kieSession.setGlobal("profitsharingRuleResponse" ,profitsharingRuleResponse); kieSession.insert(profitsharingRuleRequest); kieSession.fireAllRules(); kieSession.dispose(); ProfitsharingRuleResponseVo profitsharingRuleResponseVo = new ProfitsharingRuleResponseVo (); BeanUtils.copyProperties(profitsharingRuleResponse,profitsharingRuleResponseVo); return profitsharingRuleResponseVo; }
远程调用 ProfitsharingRuleFeignClient
1 2 3 4 5 6 7 @PostMapping("/rules/profitsharing/calculateOrderProfitsharingFee") Result<ProfitsharingRuleResponseVo> calculateOrderProfitsharingFee (@RequestBody ProfitsharingRuleRequestForm profitsharingRuleRequestForm) ;
结束服务更新订单
更新订单数据:订单状态、订单实际距离、订单实际金额等
添加实际账单信息
添加分账信息
订单微服务接口 OrderInfoController
1 2 3 4 5 @Operation(summary = "结束代驾服务更新订单账单") @PostMapping("/endDrive") public Result<Boolean> endDrive (@RequestBody UpdateOrderBillForm updateOrderBillForm) { return Result.ok(orderInfoService.endDrive(updateOrderBillForm)); }
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 @Autowired private OrderBillMapper orderBillMapper;@Autowired private OrderProfitsharingMapper orderProfitsharingMapper;@Override public Boolean endDrive (UpdateOrderBillForm updateOrderBillForm) { LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper <>(); wrapper.eq(OrderInfo::getId,updateOrderBillForm.getOrderId()); wrapper.eq(OrderInfo::getDriverId,updateOrderBillForm.getDriverId()); OrderInfo orderInfo = new OrderInfo (); orderInfo.setStatus(OrderStatus.END_SERVICE.getStatus()); orderInfo.setRealAmount(updateOrderBillForm.getTotalAmount()); orderInfo.setFavourFee(updateOrderBillForm.getFavourFee()); orderInfo.setRealDistance(updateOrderBillForm.getRealDistance()); orderInfo.setEndServiceTime(new Date ()); int rows = orderInfoMapper.update(orderInfo, wrapper); if (rows == 1 ) { OrderBill orderBill = new OrderBill (); BeanUtils.copyProperties(updateOrderBillForm,orderBill); orderBill.setOrderId(updateOrderBillForm.getOrderId()); orderBill.setPayAmount(updateOrderBillForm.getTotalAmount()); orderBillMapper.insert(orderBill); OrderProfitsharing orderProfitsharing = new OrderProfitsharing (); BeanUtils.copyProperties(updateOrderBillForm, orderProfitsharing); orderProfitsharing.setOrderId(updateOrderBillForm.getOrderId()); orderProfitsharing.setRuleId(updateOrderBillForm.getProfitsharingRuleId()); orderProfitsharing.setStatus(1 ); orderProfitsharingMapper.insert(orderProfitsharing); } else { throw new GuiguException (ResultCodeEnum.UPDATE_ERROR); } return true ; }
远程调用 OrderInfoFeignClient
1 2 3 4 5 6 7 @PostMapping("/order/info/endDrive") Result<Boolean> endDrive (@RequestBody UpdateOrderBillForm updateOrderBillForm) ;
结束服务 web-driver中的OrderController
1 2 3 4 5 6 7 8 @Operation(summary = "结束代驾服务更新订单账单") @LoginDetection @PostMapping("/endDrive") public Result<Boolean> endDrive (@RequestBody OrderFeeForm orderFeeForm) { Long driverId = AuthContextHolder.getUserId(); orderFeeForm.setDriverId(driverId); return Result.ok(orderService.endDrive(orderFeeForm)); }
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 @Autowired private LocationFeignClient locationFeignClient; @Autowired private FeeRuleFeignClient feeRuleFeignClient; @Autowired private RewardRuleFeignClient rewardRuleFeignClient; @Autowired private ProfitsharingRuleFeignClient profitsharingRuleFeignClient;@Override public Boolean endDrive (OrderFeeForm orderFeeForm) { OrderInfo orderInfo = orderInfoFeignClient.getOrderInfo(orderFeeForm.getOrderId()).getData(); if (orderInfo.getDriverId() != orderFeeForm.getDriverId()) { throw new GuiguException (ResultCodeEnum.ILLEGAL_REQUEST); } BigDecimal realDistance = locationFeignClient.calculateOrderRealDistance(orderFeeForm.getOrderId()).getData(); FeeRuleRequestForm feeRuleRequestForm = new FeeRuleRequestForm (); feeRuleRequestForm.setDistance(realDistance); feeRuleRequestForm.setStartTime(orderInfo.getStartServiceTime()); Integer waitMinute = Math.abs((int )((orderInfo.getStartServiceTime().getTime()-orderInfo.getArriveTime().getTime())/(1000 * 60 ))); feeRuleRequestForm.setWaitMinute(waitMinute); FeeRuleResponseVo feeRuleResponseVo = feeRuleFeignClient.calculateOrderFee(feeRuleRequestForm).getData(); BigDecimal totalAmount = feeRuleResponseVo.getTotalAmount().add(orderFeeForm.getTollFee()) .add(orderFeeForm.getParkingFee()) .add(orderFeeForm.getOtherFee()) .add(orderInfo.getFavourFee()); feeRuleResponseVo.setTotalAmount(totalAmount); String startTime = new DateTime (orderInfo.getStartServiceTime()).toString("yyyy-MM-dd" ) + " 00:00:00" ; String endTime = new DateTime (orderInfo.getStartServiceTime()).toString("yyyy-MM-dd" ) + " 24:00:00" ; Long orderNum = orderInfoFeignClient.getOrderNumByTime(startTime, endTime).getData(); RewardRuleRequestForm rewardRuleRequestForm = new RewardRuleRequestForm (); rewardRuleRequestForm.setStartTime(orderInfo.getStartServiceTime()); rewardRuleRequestForm.setOrderNum(orderNum); RewardRuleResponseVo rewardRuleResponseVo = rewardRuleFeignClient.calculateOrderRewardFee(rewardRuleRequestForm).getData(); ProfitsharingRuleRequestForm profitsharingRuleRequestForm = new ProfitsharingRuleRequestForm (); profitsharingRuleRequestForm.setOrderAmount(feeRuleResponseVo.getTotalAmount()); profitsharingRuleRequestForm.setOrderNum(orderNum); ProfitsharingRuleResponseVo profitsharingRuleResponseVo = profitsharingRuleFeignClient.calculateOrderProfitsharingFee(profitsharingRuleRequestForm).getData(); UpdateOrderBillForm updateOrderBillForm = new UpdateOrderBillForm (); updateOrderBillForm.setOrderId(orderFeeForm.getOrderId()); updateOrderBillForm.setDriverId(orderFeeForm.getDriverId()); updateOrderBillForm.setTollFee(orderFeeForm.getTollFee()); updateOrderBillForm.setParkingFee(orderFeeForm.getParkingFee()); updateOrderBillForm.setOtherFee(orderFeeForm.getOtherFee()); updateOrderBillForm.setFavourFee(orderInfo.getFavourFee()); updateOrderBillForm.setRealDistance(realDistance); BeanUtils.copyProperties(rewardRuleResponseVo, updateOrderBillForm); BeanUtils.copyProperties(feeRuleResponseVo, updateOrderBillForm); BeanUtils.copyProperties(profitsharingRuleResponseVo, updateOrderBillForm); updateOrderBillForm.setProfitsharingRuleId(profitsharingRuleResponseVo.getProfitsharingRuleId()); orderInfoFeignClient.endDrive(updateOrderBillForm); return true ; }
添加位置限定 修改之前web-driver中OrderServiceImpl编写的方法,在开始订单做判断
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Override public Boolean driverArriveStartLocation (Long orderId, Long driverId) { OrderInfo orderInfo = orderInfoFeignClient.getOrderInfo(orderId).getData(); OrderLocationVo orderLocationVo = locationFeignClient.getCacheOrderLocation(orderId).getData(); double distance = LocationUtil.getDistance(orderInfo.getStartPointLatitude().doubleValue(), orderInfo.getStartPointLongitude().doubleValue(), orderLocationVo.getLatitude().doubleValue(), orderLocationVo.getLongitude().doubleValue()); if (distance > SystemConstant.DRIVER_START_LOCATION_DISTION) { throw new GuiguException (ResultCodeEnum.DRIVER_START_LOCATION_DISTION_ERROR); } return orderInfoFeignClient.driverArriveStartLocation(orderId,driverId).getData(); }
结束订单也是一样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 @Override public Boolean endDrive (OrderFeeForm orderFeeForm) { OrderInfo orderInfo = orderInfoFeignClient.getOrderInfo(orderFeeForm.getOrderId()).getData(); if (orderInfo.getDriverId() != orderFeeForm.getDriverId()) { throw new GuiguException (ResultCodeEnum.ILLEGAL_REQUEST); } OrderServiceLastLocationVo orderServiceLastLocationVo = locationFeignClient.getOrderServiceLastLocation(orderFeeForm.getOrderId()).getData(); double distance = LocationUtil.getDistance(orderInfo.getEndPointLatitude().doubleValue(), orderInfo.getEndPointLongitude().doubleValue(), orderServiceLastLocationVo.getLatitude().doubleValue(), orderServiceLastLocationVo.getLongitude().doubleValue()); if (distance > SystemConstant.DRIVER_END_LOCATION_DISTION) { throw new GuiguException (ResultCodeEnum.DRIVER_END_LOCATION_DISTION_ERROR); } BigDecimal realDistance = locationFeignClient.calculateOrderRealDistance(orderFeeForm.getOrderId()).getData(); FeeRuleRequestForm feeRuleRequestForm = new FeeRuleRequestForm (); feeRuleRequestForm.setDistance(realDistance); feeRuleRequestForm.setStartTime(orderInfo.getStartServiceTime()); Integer waitMinute = Math.abs((int )((orderInfo.getStartServiceTime().getTime()-orderInfo.getArriveTime().getTime())/(1000 * 60 ))); feeRuleRequestForm.setWaitMinute(waitMinute); FeeRuleResponseVo feeRuleResponseVo = feeRuleFeignClient.calculateOrderFee(feeRuleRequestForm).getData(); BigDecimal totalAmount = feeRuleResponseVo.getTotalAmount().add(orderFeeForm.getTollFee()) .add(orderFeeForm.getParkingFee()) .add(orderFeeForm.getOtherFee()) .add(orderInfo.getFavourFee()); feeRuleResponseVo.setTotalAmount(totalAmount); String startTime = new DateTime (orderInfo.getStartServiceTime()).toString("yyyy-MM-dd" ) + " 00:00:00" ; String endTime = new DateTime (orderInfo.getStartServiceTime()).toString("yyyy-MM-dd" ) + " 24:00:00" ; Long orderNum = orderInfoFeignClient.getOrderNumByTime(startTime, endTime).getData(); RewardRuleRequestForm rewardRuleRequestForm = new RewardRuleRequestForm (); rewardRuleRequestForm.setStartTime(orderInfo.getStartServiceTime()); rewardRuleRequestForm.setOrderNum(orderNum); RewardRuleResponseVo rewardRuleResponseVo = rewardRuleFeignClient.calculateOrderRewardFee(rewardRuleRequestForm).getData(); ProfitsharingRuleRequestForm profitsharingRuleRequestForm = new ProfitsharingRuleRequestForm (); profitsharingRuleRequestForm.setOrderAmount(feeRuleResponseVo.getTotalAmount()); profitsharingRuleRequestForm.setOrderNum(orderNum); ProfitsharingRuleResponseVo profitsharingRuleResponseVo = profitsharingRuleFeignClient.calculateOrderProfitsharingFee(profitsharingRuleRequestForm).getData(); UpdateOrderBillForm updateOrderBillForm = new UpdateOrderBillForm (); updateOrderBillForm.setOrderId(orderFeeForm.getOrderId()); updateOrderBillForm.setDriverId(orderFeeForm.getDriverId()); updateOrderBillForm.setTollFee(orderFeeForm.getTollFee()); updateOrderBillForm.setParkingFee(orderFeeForm.getParkingFee()); updateOrderBillForm.setOtherFee(orderFeeForm.getOtherFee()); updateOrderBillForm.setFavourFee(orderInfo.getFavourFee()); updateOrderBillForm.setRealDistance(realDistance); BeanUtils.copyProperties(rewardRuleResponseVo, updateOrderBillForm); BeanUtils.copyProperties(feeRuleResponseVo, updateOrderBillForm); BeanUtils.copyProperties(profitsharingRuleResponseVo, updateOrderBillForm); updateOrderBillForm.setProfitsharingRuleId(profitsharingRuleResponseVo.getProfitsharingRuleId()); orderInfoFeignClient.endDrive(updateOrderBillForm); return true ; }
我的订单和异步编排 我的订单
乘客端或司机端,都有我的订单,都可以查看用户所有的订单
乘客端我的订单 订单微服务接口 OrderInfoController
1 2 3 4 5 6 7 8 9 10 11 12 13 @Operation(summary = "获取乘客订单分页列表") @GetMapping("/findCustomerOrderPage/{customerId}/{page}/{limit}") public Result<PageVo> findCustomerOrderPage (@PathVariable Long customerId, @PathVariable Long page, @PathVariable Long limit) { Page<OrderInfo> pageParam = new Page <>(page,limit); PageVo pageVo = orderInfoService.findCustomerOrderPage(pageParam,customerId); pageVo.setPage(page); pageVo.setLimit(limit); return Result.ok(pageVo); }
service
1 2 3 4 5 6 @Override public PageVo findCustomerOrderPage (Page<OrderInfo> pageParam, Long customerId) { IPage<OrderListVo> pageInfo = orderInfoMapper.selectCustomerOrderPage(pageParam,customerId); return new PageVo <>(pageInfo.getRecords(),pageInfo.getPages(),pageInfo.getTotal()); }
远程调用 OrderInfoFeignClient
1 2 3 4 5 6 7 8 9 10 11 @GetMapping("/order/info/findCustomerOrderPage/{customerId}/{page}/{limit}") Result<PageVo> findCustomerOrderPage (@PathVariable("customerId") Long customerId, @PathVariable("page") Long page, @PathVariable("limit") Long limit) ;
乘客web端调用 OrderController
1 2 3 4 5 6 7 8 9 10 11 12 13 @Operation(summary = "获取乘客订单分页列表") @LoginDetection @GetMapping("findCustomerOrderPage/{page}/{limit}") public Result<PageVo> findCustomerOrderPage ( @Parameter(name = "page", description = "当前页码", required = true) @PathVariable Long page, @Parameter(name = "limit", description = "每页记录数", required = true) @PathVariable Long limit) { Long customerId = AuthContextHolder.getUserId(); PageVo pageVo = orderService.findCustomerOrderPage(customerId, page, limit); return Result.ok(pageVo); }
service
1 2 3 4 5 @Override public PageVo findCustomerOrderPage (Long customerId, Long page, Long limit) { return orderInfoFeignClient.findCustomerOrderPage(customerId,page,limit).getData(); }
xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <select id ="selectCustomerOrderPage" resultType ="com.atguigu.daijia.model.vo.order.OrderListVo" > select info.id, info.order_no, info.start_location, info.end_location, if(info.status < 7, info.expect_amount, bill.pay_amount) as amount, info.status, info.create_time from order_info info left join order_bill bill on info.id = bill.order_id where info.customer_id = #{customerId} and info.is_deleted =0 order by info.create_time desc </select >
司机端我的订单 订单微服务接口 OrderInfoController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Operation(summary = "获取司机订单分页列表") @GetMapping("/findDriverOrderPage/{driverId}/{page}/{limit}") public Result<PageVo> findDriverOrderPage ( @Parameter(name = "driverId", description = "司机id", required = true) @PathVariable Long driverId, @Parameter(name = "page", description = "当前页码", required = true) @PathVariable Long page, @Parameter(name = "limit", description = "每页记录数", required = true) @PathVariable Long limit) { Page<OrderInfo> pageParam = new Page <>(page, limit); PageVo pageVo = orderInfoService.findDriverOrderPage(pageParam, driverId); pageVo.setPage(page); pageVo.setLimit(limit); return Result.ok(pageVo); }
service
1 2 3 4 5 6 @Override public PageVo findDriverOrderPage (Page<OrderInfo> pageParam, Long driverId) { IPage<OrderListVo> pageInfo = orderInfoMapper.selectDriverOrderPage(pageParam,driverId); return new PageVo <>(pageInfo.getRecords(),pageInfo.getPages(),pageInfo.getTotal()); }
xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <select id ="selectDriverOrderPage" resultType ="com.atguigu.daijia.model.vo.order.OrderListVo" > select info.id, info.order_no, info.start_location, info.end_location, real_amount as pay_amount, if(info.status < 7, info.expect_amount, info.real_amount) as amount, info.status, info.create_time from order_info info where info.driver_id = #{driverId} and info.is_deleted =0 order by info.create_time desc </select >
远程调用 OrderInfoFeignClient
1 2 3 4 5 6 7 8 9 10 11 @GetMapping("/order/info/findDriverOrderPage/{driverId}/{page}/{limit}") Result<PageVo> findDriverOrderPage (@PathVariable("driverId") Long driverId, @PathVariable("page") Long page, @PathVariable("limit") Long limit) ;
司机端web接口 OrderController
1 2 3 4 5 6 7 8 9 10 11 12 @Operation(summary = "获取司机订单分页列表") @LoginDetection @GetMapping("findDriverOrderPage/{page}/{limit}") public Result<PageVo> findDriverOrderPage ( @Parameter(name = "page", description = "当前页码", required = true) @PathVariable Long page, @Parameter(name = "limit", description = "每页记录数", required = true) @PathVariable Long limit) { Long driverId = AuthContextHolder.getUserId(); PageVo pageVo = orderService.findDriverOrderPage(driverId, page, limit); return Result.ok(pageVo); }
service
1 2 3 4 5 @Override public PageVo findDriverOrderPage (Long driverId, Long page, Long limit) { return orderInfoFeignClient.findDriverOrderPage(driverId,page,limit).getData(); }
异步编排
问题:司机结束订单后会有大量的远程调用,如果按照流程来会浪费大量时间
解决:使用多线程方式来完成这些操作
创建自定义线程池 在config包下创建类ThreadPoolConfig
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Configuration public class ThreadPoolConfig { @Bean public ThreadPoolExecutor threadPoolExecutor () { int processors = Runtime.getRuntime().availableProcessors(); ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor ( processors+1 , processors+1 , 0 , TimeUnit.SECONDS, new ArrayBlockingQueue <>(3 ), Executors.defaultThreadFactory(), new ThreadPoolExecutor .AbortPolicy() ); return threadPoolExecutor; } }
修改web-driver之前的代码 修改前面driver-web里OrderServiceImpl的endDrive方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 @SneakyThrows public Boolean endDrive (OrderFeeForm orderFeeForm) { CompletableFuture<OrderInfo> orderInfoCompletableFuture = CompletableFuture.supplyAsync(() -> { OrderInfo orderInfo = orderInfoFeignClient.getOrderInfo(orderFeeForm.getOrderId()).getData(); if (orderInfo.getDriverId() != orderFeeForm.getDriverId()) { throw new GuiguException (ResultCodeEnum.ILLEGAL_REQUEST); } return orderInfo; }); CompletableFuture<OrderServiceLastLocationVo> orderServiceLastLocationVoCompletableFuture = CompletableFuture.supplyAsync(() -> { OrderServiceLastLocationVo orderServiceLastLocationVo = locationFeignClient.getOrderServiceLastLocation(orderFeeForm.getOrderId()).getData(); return orderServiceLastLocationVo; }); CompletableFuture.allOf(orderInfoCompletableFuture, orderServiceLastLocationVoCompletableFuture).join(); OrderInfo orderInfo = orderInfoCompletableFuture.get(); OrderServiceLastLocationVo orderServiceLastLocationVo = orderServiceLastLocationVoCompletableFuture.get(); double distance = LocationUtil.getDistance(orderInfo.getEndPointLatitude().doubleValue(), orderInfo.getEndPointLongitude().doubleValue(), orderServiceLastLocationVo.getLatitude().doubleValue(), orderServiceLastLocationVo.getLongitude().doubleValue()); if (distance > SystemConstant.DRIVER_END_LOCATION_DISTION) { throw new GuiguException (ResultCodeEnum.DRIVER_END_LOCATION_DISTION_ERROR); } CompletableFuture<BigDecimal> realDistanceCompletableFuture = CompletableFuture.supplyAsync(() -> { BigDecimal realDistance = locationFeignClient.calculateOrderRealDistance(orderFeeForm.getOrderId()).getData(); return realDistance; }); CompletableFuture<FeeRuleResponseVo> feeRuleResponseVoCompletableFuture = realDistanceCompletableFuture.thenApplyAsync((realDistance) -> { FeeRuleRequestForm feeRuleRequestForm = new FeeRuleRequestForm (); feeRuleRequestForm.setDistance(realDistance); feeRuleRequestForm.setStartTime(orderInfo.getStartServiceTime()); Math.abs((int ) ((orderInfo.getArriveTime().getTime() - orderInfo.getAcceptTime().getTime()) / (1000 * 60 ))); feeRuleRequestForm.setWaitMinute(waitMinute); FeeRuleResponseVo feeRuleResponseVo = feeRuleFeignClient.calculateOrderFee(feeRuleRequestForm).getData(); BigDecimal totalAmount = feeRuleResponseVo.getTotalAmount().add(orderFeeForm.getTollFee()) .add(orderFeeForm.getParkingFee()) .add(orderFeeForm.getOtherFee()) .add(orderInfo.getFavourFee()); feeRuleResponseVo.setTotalAmount(totalAmount); return feeRuleResponseVo; }); CompletableFuture<Long> orderNumCompletableFuture = CompletableFuture.supplyAsync(() -> { String startTime = new DateTime (orderInfo.getStartServiceTime()).toString("yyyy-MM-dd" ) + " 00:00:00" ; String endTime = new DateTime (orderInfo.getStartServiceTime()).toString("yyyy-MM-dd" ) + " 24:00:00" ; Long orderNum = orderInfoFeignClient.getOrderNumByTime(startTime, endTime).getData(); return orderNum; }); CompletableFuture<RewardRuleResponseVo> rewardRuleResponseVoCompletableFuture = orderNumCompletableFuture.thenApplyAsync((orderNum) -> { RewardRuleRequestForm rewardRuleRequestForm = new RewardRuleRequestForm (); rewardRuleRequestForm.setStartTime(orderInfo.getStartServiceTime()); rewardRuleRequestForm.setOrderNum(orderNum); RewardRuleResponseVo rewardRuleResponseVo = rewardRuleFeignClient.calculateOrderRewardFee(rewardRuleRequestForm).getData(); return rewardRuleResponseVo; }); CompletableFuture<ProfitsharingRuleResponseVo> profitsharingRuleResponseVoCompletableFuture = feeRuleResponseVoCompletableFuture.thenCombineAsync(orderNumCompletableFuture, (feeRuleResponseVo, orderNum) -> { ProfitsharingRuleRequestForm profitsharingRuleRequestForm = new ProfitsharingRuleRequestForm (); profitsharingRuleRequestForm.setOrderAmount(feeRuleResponseVo.getTotalAmount()); profitsharingRuleRequestForm.setOrderNum(orderNum); ProfitsharingRuleResponseVo profitsharingRuleResponseVo = profitsharingRuleFeignClient.calculateOrderProfitsharingFee(profitsharingRuleRequestForm).getData(); return profitsharingRuleResponseVo; }); CompletableFuture.allOf( orderInfoCompletableFuture, realDistanceCompletableFuture, feeRuleResponseVoCompletableFuture, orderNumCompletableFuture, rewardRuleResponseVoCompletableFuture, profitsharingRuleResponseVoCompletableFuture ).join(); BigDecimal realDistance = realDistanceCompletableFuture.get(); FeeRuleResponseVo feeRuleResponseVo = feeRuleResponseVoCompletableFuture.get(); RewardRuleResponseVo rewardRuleResponseVo = rewardRuleResponseVoCompletableFuture.get(); ProfitsharingRuleResponseVo profitsharingRuleResponseVo = profitsharingRuleResponseVoCompletableFuture.get(); UpdateOrderBillForm updateOrderBillForm = new UpdateOrderBillForm (); updateOrderBillForm.setOrderId(orderFeeForm.getOrderId()); updateOrderBillForm.setDriverId(orderFeeForm.getDriverId()); updateOrderBillForm.setTollFee(orderFeeForm.getTollFee()); updateOrderBillForm.setParkingFee(orderFeeForm.getParkingFee()); updateOrderBillForm.setOtherFee(orderFeeForm.getOtherFee()); updateOrderBillForm.setFavourFee(orderInfo.getFavourFee()); updateOrderBillForm.setRealDistance(realDistance); BeanUtils.copyProperties(rewardRuleResponseVo, updateOrderBillForm); BeanUtils.copyProperties(feeRuleResponseVo, updateOrderBillForm); BeanUtils.copyProperties(profitsharingRuleResponseVo, updateOrderBillForm); updateOrderBillForm.setProfitsharingRuleId(profitsharingRuleResponseVo.getProfitsharingRuleId()); orderInfoFeignClient.endDrive(updateOrderBillForm); return true ; }
订单支付 账单信息
司机结束代价后,生成账单(包含账单信息和分账信息)
获取账单信息 订单微服务接口 OrderInfoController
1 2 3 4 5 @Operation(summary = "根据订单id获取实际账单信息") @GetMapping("/getOrderBillInfo/{orderId}") public Result<OrderBillVo> getOrderBillInfo (@PathVariable Long orderId) { return Result.ok(orderInfoService.getOrderBillInfo(orderId)); }
service
1 2 3 4 5 6 7 8 9 10 11 @Override public OrderBillVo getOrderBillInfo (Long orderId) { LambdaQueryWrapper<OrderBill> wrapper = new LambdaQueryWrapper <>(); wrapper.eq(OrderBill::getOrderId,orderId); OrderBill orderBill = orderBillMapper.selectOne(wrapper); OrderBillVo orderBillVo = new OrderBillVo (); BeanUtils.copyProperties(orderBill,orderBillVo); return orderBillVo; }
远程调用 OrderInfoFeignClient
1 2 3 4 5 6 7 @GetMapping("/order/info/getOrderBillInfo/{orderId}") Result<OrderBillVo> getOrderBillInfo (@PathVariable("orderId") Long orderId) ;
获取分账信息 订单微服务接口 OrderInfoController
1 2 3 4 5 @Operation(summary = "根据订单id获取实际分账信息") @GetMapping("/getOrderProfitsharing/{orderId}") public Result<OrderProfitsharingVo> getOrderProfitsharing (@PathVariable Long orderId) { return Result.ok(orderInfoService.getOrderProfitsharing(orderId)); }
service
1 2 3 4 5 6 7 8 9 10 11 @Override public OrderProfitsharingVo getOrderProfitsharing (Long orderId) { LambdaQueryWrapper<OrderProfitsharing> wrapper = new LambdaQueryWrapper <>(); wrapper.eq(OrderProfitsharing::getOrderId,orderId); OrderProfitsharing orderProfitsharing = orderProfitsharingMapper.selectOne(wrapper); OrderProfitsharingVo orderProfitsharingVo = new OrderProfitsharingVo (); BeanUtils.copyProperties(orderProfitsharing,orderProfitsharingVo); return orderProfitsharingVo; }
远程调用 OrderInfoFeignClient
1 2 3 4 5 6 7 @GetMapping("/order/info/getOrderProfitsharing/{orderId}") Result<OrderProfitsharingVo> getOrderProfitsharing (@PathVariable("orderId") Long orderId) ;
司机端获取账单信息 方法之前有,只需要改service OrderController`
1 2 3 4 5 6 7 @Operation(summary = "获取订单账单详细信息") @LoginDetection @GetMapping("/getOrderInfo/{orderId}") public Result<OrderInfoVo> getOrderInfo (@PathVariable Long orderId) { Long driverId = AuthContextHolder.getUserId(); return Result.ok(orderService.getOrderInfo(orderId, driverId)); }
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 @Override public OrderInfoVo getOrderInfo (Long orderId, Long driverId) { OrderInfo orderInfo = orderInfoFeignClient.getOrderInfo(orderId).getData(); if (orderInfo.getDriverId() != driverId) { throw new GuiguException (ResultCodeEnum.ILLEGAL_REQUEST); } OrderBillVo orderBillVo = null ; OrderProfitsharingVo orderProfitsharingVo = null ; if (orderInfo.getStatus() >= OrderStatus.END_SERVICE.getStatus()) { orderBillVo = orderInfoFeignClient.getOrderBillInfo(orderId).getData(); orderProfitsharingVo = orderInfoFeignClient.getOrderProfitsharing(orderId).getData(); } OrderInfoVo orderInfoVo = new OrderInfoVo (); orderInfoVo.setOrderId(orderId); BeanUtils.copyProperties(orderInfo,orderInfoVo); orderInfoVo.setOrderBillVo(orderBillVo); orderInfoVo.setOrderProfitsharingVo(orderProfitsharingVo); return orderInfoVo; }
司机发送账单 订单微服务接口 OrderInfoController
1 2 3 4 5 @Operation(summary = "发送账单信息") @GetMapping("/sendOrderBillInfo/{orderId}/{driverId}") Result<Boolean> sendOrderBillInfo (@PathVariable Long orderId, @PathVariable Long driverId) { return Result.ok(orderInfoService.sendOrderBillInfo(orderId, driverId)); }
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Override public Boolean sendOrderBillInfo (Long orderId, Long driverId) { LambdaQueryWrapper<OrderInfo> queryWrapper = new LambdaQueryWrapper <>(); queryWrapper.eq(OrderInfo::getId, orderId); queryWrapper.eq(OrderInfo::getDriverId, driverId); OrderInfo updateOrderInfo = new OrderInfo (); updateOrderInfo.setStatus(OrderStatus.UNPAID.getStatus()); int row = orderInfoMapper.update(updateOrderInfo, queryWrapper); if (row == 1 ) { return true ; } else { throw new GuiguException (ResultCodeEnum.UPDATE_ERROR); } }
远程调用 OrderInfoFeignClient
1 2 3 4 5 6 7 8 @GetMapping("/order/info/sendOrderBillInfo/{orderId}/{driverId}") Result<Boolean> sendOrderBillInfo (@PathVariable("orderId") Long orderId, @PathVariable("driverId") Long driverId) ;
司机web端调用 OrderController
1 2 3 4 5 6 7 @Operation(summary = "司机发送账单信息") @LoginDetection @GetMapping("/sendOrderBillInfo/{orderId}") public Result<Boolean> sendOrderBillInfo (@PathVariable Long orderId) { Long driverId = AuthContextHolder.getUserId(); return Result.ok(orderService.sendOrderBillInfo(orderId, driverId)); }
service
1 2 3 4 5 @Override public Boolean sendOrderBillInfo (Long orderId, Long driverId) { return orderInfoFeignClient.sendOrderBillInfo(orderId, driverId).getData(); }
乘客获取账单 也是修改之前写过的代码,只需要修改service OrderController
1 2 3 4 5 6 7 @Operation(summary = "获取订单信息") @LoginDetection @GetMapping("/getOrderInfo/{orderId}") public Result<OrderInfoVo> getOrderInfo (@PathVariable Long orderId) { Long customerId = AuthContextHolder.getUserId(); return Result.ok(orderService.getOrderInfo(orderId, customerId)); }
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 @Override public OrderInfoVo getOrderInfo (Long orderId, Long customerId) { OrderInfo orderInfo = orderInfoFeignClient.getOrderInfo(orderId).getData(); if (orderInfo.getCustomerId() != customerId) { throw new GuiguException (ResultCodeEnum.ILLEGAL_REQUEST); } DriverInfoVo driverInfoVo = null ; Long driverId = orderInfo.getDriverId(); if (driverId != null ) { driverInfoVo = driverInfoFeignClient.getDriverInfo(driverId).getData(); } OrderBillVo orderBillVo = null ; if (orderInfo.getStatus() >= OrderStatus.UNPAID.getStatus()) { orderBillVo = orderInfoFeignClient.getOrderBillInfo(orderId).getData(); } OrderInfoVo orderInfoVo = new OrderInfoVo (); orderInfoVo.setOrderId(orderId); BeanUtils.copyProperties(orderInfo,orderInfoVo); orderInfoVo.setOrderBillVo(orderBillVo); orderInfoVo.setDriverInfoVo(driverInfoVo); return orderInfoVo; }
微信支付 准备接口 获取乘客openid service-customer中的CustomerInfoController
1 2 3 4 5 @Operation(summary = "获取客户OpenId") @GetMapping("/getCustomerOpenId/{customerId}") public Result<String> getCustomerOpenId (@PathVariable Long customerId) { return Result.ok(customerInfoService.getCustomerOpenId(customerId)); }
service
1 2 3 4 5 6 7 8 @Override public String getCustomerOpenId (Long customerId) { LambdaQueryWrapper<CustomerInfo> wrapper = new LambdaQueryWrapper <>(); wrapper.eq(CustomerInfo::getId,customerId); CustomerInfo customerInfo = customerInfoMapper.selectOne(wrapper); return customerInfo.getWxOpenId(); }
远程调用
CustomerInfoFeignClient
1 2 3 4 5 6 7 @GetMapping("/customer/info/getCustomerOpenId/{customerId}") Result<String> getCustomerOpenId (@PathVariable("customerId") Long customerId) ;
获取司机openid service-driver中DriverInfoController
1 2 3 4 5 @Operation(summary = "获取司机OpenId") @GetMapping("/getDriverOpenId/{driverId}") public Result<String> getDriverOpenId (@PathVariable Long driverId) { return Result.ok(driverInfoService.getDriverOpenId(driverId)); }
serivce
1 2 3 4 5 6 @Override public String getDriverOpenId (Long driverId) { DriverInfo driverInfo = this .getOne(new LambdaQueryWrapper <DriverInfo>().eq(DriverInfo::getId, driverId).select(DriverInfo::getWxOpenId)); return driverInfo.getWxOpenId(); }
远程调用
DriverInfoFeignClient
1 2 3 4 5 6 7 @GetMapping("/driver/info/getDriverOpenId/{driverId}") Result<String> getDriverOpenId (@PathVariable("driverId") Long driverId) ;
获取支付信息 service-order中OrderInfoController
1 2 3 4 5 @Operation(summary = "获取订单支付信息") @GetMapping("/getOrderPayVo/{orderNo}/{customerId}") public Result<OrderPayVo> getOrderPayVo (@PathVariable String orderNo, @PathVariable Long customerId) { return Result.ok(orderInfoService.getOrderPayVo(orderNo, customerId)); }
service
1 2 3 4 5 6 7 8 9 10 @Override public OrderPayVo getOrderPayVo (String orderNo, Long customerId) { OrderPayVo orderPayVo = orderInfoMapper.selectOrderPayVo(orderNo,customerId); if (orderPayVo != null ) { String content = orderPayVo.getStartLocation() + " 到 " +orderPayVo.getEndLocation(); orderPayVo.setContent(content); } return orderPayVo; }
远程调用
1 2 3 4 5 6 7 8 @GetMapping("/order/info/getOrderPayVo/{orderNo}/{customerId}") Result<OrderPayVo> getOrderPayVo (@PathVariable("orderNo") String orderNo, @PathVariable("customerId") Long customerId) ;
微信支付接口 在service-payment下
导入依赖
1 2 3 4 <dependency> <groupId>com.github.wechatpay-apiv3</groupId> <artifactId>wechatpay-java</artifactId> </dependency>
创建配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 @Configuration @ConfigurationProperties(prefix="wx.v3pay") @Data public class WxPayV3Properties { private String appid; public String merchantId; public String privateKeyPath; public String merchantSerialNumber; public String apiV3key; private String notifyUrl; @Bean public RSAAutoCertificateConfig getConfig () { return new RSAAutoCertificateConfig .Builder() .merchantId(this .getMerchantId()) .privateKeyFromPath(this .getPrivateKeyPath()) .merchantSerialNumber(this .getMerchantSerialNumber()) .apiV3Key(this .getApiV3key()) .build(); } }
WxPayController
1 2 3 4 5 6 7 8 @Autowired private WxPayService wxPayService; @Operation(summary = "创建微信支付") @PostMapping("/createJsapi") public Result<WxPrepayVo> createWxPayment (@RequestBody PaymentInfoForm paymentInfoForm) { return Result.ok(wxPayService.createWxPayment(paymentInfoForm)); }
WxPayFeignClient
1 2 3 4 5 6 7 @PostMapping("/payment/wxPay/createWxPayment") Result<WxPrepayVo> createWxPayment (@RequestBody PaymentInfoForm paymentInfoForm) ;
OrderController
1 2 3 4 5 6 7 8 @Operation(summary = "创建微信支付") @LoginDetection @PostMapping("/createWxPayment") public Result<WxPrepayVo> createWxPayment (@RequestBody CreateWxPaymentForm createWxPaymentForm) { Long customerId = AuthContextHolder.getUserId(); createWxPaymentForm.setCustomerId(customerId); return Result.ok(orderService.createWxPayment(createWxPaymentForm)); }
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 @Override public WxPrepayVo createWxPayment (CreateWxPaymentForm createWxPaymentForm) { OrderPayVo orderPayVo = orderInfoFeignClient.getOrderPayVo(createWxPaymentForm.getOrderNo(), createWxPaymentForm.getCustomerId()).getData(); if (orderPayVo.getStatus() != OrderStatus.UNPAID.getStatus()) { throw new GuiguException (ResultCodeEnum.ILLEGAL_REQUEST); } String customerOpenId = customerInfoFeignClient.getCustomerOpenId(orderPayVo.getCustomerId()).getData(); String driverOpenId = driverInfoFeignClient.getDriverOpenId(orderPayVo.getDriverId()).getData(); PaymentInfoForm paymentInfoForm = new PaymentInfoForm (); paymentInfoForm.setCustomerOpenId(customerOpenId); paymentInfoForm.setDriverOpenId(driverOpenId); paymentInfoForm.setOrderNo(orderPayVo.getOrderNo()); paymentInfoForm.setAmount(orderPayVo.getPayAmount()); paymentInfoForm.setContent(orderPayVo.getContent()); paymentInfoForm.setPayWay(1 ); WxPrepayVo wxPrepayVo = wxPayFeignClient.createWxPayment(paymentInfoForm).getData(); return wxPrepayVo; }
支付结果查询